# 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= ``` `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.