Files
2026-09-15 15:24:03 -04:00

4.5 KiB

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 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:

    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:

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.