Initial Commit

This commit is contained in:
2026-09-15 15:24:03 -04:00
commit 4dc7086e7e
605 changed files with 101380 additions and 0 deletions
+108
View File
@@ -0,0 +1,108 @@
# Gitea — so the VM sub-agent can publish what it built
Fifteen minutes. **Optional**: without it a session still runs, still keeps its files in
the project folder on the array, and simply returns no link.
You need a Gitea instance first — see
[../deployment/gitea.compose.yml](../deployment/gitea.compose.yml) if you do not have one.
## What it is for
When a session finishes, vmd commits the project folder and pushes it to a repository named
after the project, then cuts a release. The user gets one link. The agent never touches
git: the token stays on the host, the commit runs with hooks disabled under a non-root
user, and a scratch filter keeps test scripts out of what gets published.
## Steps
1. **Create an organisation** to hold the repositories. Signed in as an admin:
*+ → New Organisation*. Call it `vmlab` or whatever you like.
Set its visibility to **Limited** if you want the repositories visible to signed-in
users only. The repositories themselves are created public *within* that org, so the
org's visibility is what controls who can see them.
2. **Create a bot account.** *Site Administration → Identity & Access → Users → Create
User*. Name it `vmagent`. Give it a long random password you will not need again, and
untick "require password change".
3. **Add the bot to the organisation** as a member with **write** access to repositories.
4. **Generate a token as the bot.** Sign in as it, then *Settings → Applications →
Generate New Token*. It needs:
- `write:repository`
- `write:organization`
Copy it now; Gitea shows it once.
5. **Put it on the host**, in `/etc/owuivm/vmd.env`:
```
OWUITOOLS_GITEA_URL=http://192.168.1.10:7200
OWUITOOLS_GITEA_PUBLIC_URL=https://git.example.com
OWUITOOLS_GITEA_ORG=vmlab
OWUITOOLS_GITEA_BOT=vmagent
OWUITOOLS_GITEA_TOKEN=<the token>
```
`configs/vmd.env` is the template this file is created from; edit the live copy, since
`install.sh` never overwrites it once it exists.
6. **Let the daemon reach it.** `vm/host/owuivmd.service` runs with
`IPAddressDeny=any` and an allowlist. Gitea's address has to be on it:
```ini
IPAddressAllow=192.168.1.10/32
IPAddressAllow=172.28.0.0/16
```
**Both lines.** The first is the LAN address you push to. The second is the Docker
network Gitea's container is on, and it is not redundant: systemd's filter is a BPF
program on the cgroup's packets, and by the time it sees them Docker's DNAT has already
rewritten the destination to the container's own address. Measured — with only the
`/32`, the connection times out.
7. `sudo ./vm/host/install.sh && sudo systemctl restart owuivmd`
## Checking it
This asks Gitea, as the daemon, using the credentials it will actually use:
```sh
sudo -u owuivmd python3 - <<'PY'
import json, urllib.request
env = {}
for line in open("/etc/owuivm/vmd.env"):
line = line.strip()
if line and not line.startswith("#") and "=" in line:
key, value = line.split("=", 1)
env[key] = value
token = env.get("OWUITOOLS_GITEA_TOKEN", "")
if not token:
raise SystemExit("no OWUITOOLS_GITEA_TOKEN in /etc/owuivm/vmd.env")
url = env.get("OWUITOOLS_GITEA_URL", "").rstrip("/")
org = env.get("OWUITOOLS_GITEA_ORG", "")
request = urllib.request.Request(f"{url}/api/v1/orgs/{org}",
headers={"Authorization": f"token {token}"})
answer = json.load(urllib.request.urlopen(request, timeout=15))
print(f"OK: reached {url} and the token can see the '{answer['username']}' organisation")
PY
```
Running it as `owuivmd` is the point — it proves the daemon can read the file *and* that
the address filter in its unit permits the connection, which is the step most likely to be
wrong. A timeout here almost always means step 6 was missed or only half done.
Otherwise, run a session and see whether the result carries a link.
## Two things worth knowing
**The token is never given to the machine or to the agent container.** vmd does the clone,
commit and push on the host and supplies the credential at push time through `GIT_ASKPASS`.
A guest that reads its own project folder finds no credential in it.
**Only the release archive downloads.** On a Gitea release page, uploaded assets are served
`inline` — a browser opens them rather than saving them, even a `.zip`. Only the
auto-generated *Source code (zip)* is served as an attachment. So what a user actually
downloads is built from the commit, which is why the scratch filter operates on the commit
rather than on the asset list.