Files
mesh-catalog/modules/gitea/module.json
T
mesh-adminandClaude Opus 5.5 48f0662fde
mesh/merge-gate head 48f0662fdea2 merged into trunk 5ce2d07c7f47 — pass: builds gitea → novox; no bus step; every machine composes with the change as it di…
mesh/repo-check head 48f0662fdea2 merged into trunk 5ce2d07c7f47 — pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-…
mesh/delivery delivered
Make the forge's tools act as an account of their own, not the administrator (issue 469, ADR 0365 clause 2)
The tools' token reached everything the administrator may do, so their boundary rested on their own guards.
The gitea module now makes a plain account, mesh-tools, with the admin login, gives it write on code, issues
and pull requests of every novox repository through one team, and mints the tools' token for it; the admin's
old token goes only after the new one is kept. The Go forge bundle sends that token, never the admin login.
The forge shows protection rules only to a repository administrator, so every check reads a branch's
protection from the branch. mesh-admin stays a ready author, so marks made before the move keep counting.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 22:28:25 +00:00

363 lines
10 KiB
JSON

{
"module": "gitea",
"version": "1",
"requires": [
"postgres-database",
"route",
"secret"
],
"contributes": {
"postgres-database": {
"name": "gitea"
},
"route": {
"web": {
"label": "git",
"endpoint": "web"
},
"internal-api-refused": {
"label": "git",
"path": "/api/internal",
"deny": true,
"priority": 100000
}
}
},
"binds": {
"postgres-database": "${dir:state}/database.json",
"route": "${dir:state}/route.json"
},
"secrets": {
"postgres-database": "${dir:state}/database.secret",
"secret": {
"internal-token": "${dir:state}/internal-token.secret",
"admin": "${dir:state}/admin.secret"
}
},
"capabilities": [
"container-runtime"
],
"emits": [
"repo.created",
"issue.opened",
"pull.merged",
"pull.updated",
"pull.closed",
"forge.push",
"forge.create",
"forge.delete",
"forge.fork",
"forge.status",
"forge.pull-request.opened",
"forge.pull-request.reopened",
"forge.pull-request.synchronized",
"forge.pull-request.edited",
"forge.pull-request.closed",
"forge.pull-request.merged",
"forge.pull-request.labeled",
"forge.pull-request.unlabeled",
"forge.pull-request.assigned",
"forge.pull-request.unassigned",
"forge.pull-request.reviewed",
"forge.pull-request.review-requested",
"forge.pull-request.commented",
"forge.pull-request.changed",
"forge.issue-comment.created",
"forge.issue-comment.edited",
"forge.issue-comment.deleted",
"forge.issues.opened",
"forge.issues.reopened",
"forge.issues.closed",
"forge.issues.edited",
"forge.issues.labeled",
"forge.issues.unlabeled",
"forge.issues.assigned",
"forge.issues.unassigned",
"forge.issues.changed",
"forge.release.published",
"forge.release.updated",
"forge.release.deleted",
"forge.release.changed",
"forge.repository.created",
"forge.repository.deleted",
"forge.repository.changed",
"forge.wiki.changed",
"forge.other"
],
"consumes": [
"mesh-controller.checked",
"node-build-agent.started"
],
"listens": [
{
"name": "web",
"port": 3000,
"protocol": "tcp",
"from": "mesh",
"why": "the forge, over http"
},
{
"name": "ssh",
"port": 22,
"protocol": "tcp",
"from": "mesh",
"why": "git over ssh, gitea's own unmodified sshd. Published on the machine's own side at 222, the mesh's fixed public convention — not 22, which the machine's own daemon holds and a module does not take"
},
{
"name": "forge-events",
"port": 3047,
"protocol": "tcp",
"from": "machine",
"why": "the forge's system webhook, every event signed with a secret the module keeps, from the forge's container on the docker bridge to the module's receiver at the bridge's gateway 172.17.0.1 (novox/hq issue 729)"
}
],
"serves": {
"npm-package-registry": {
"scheme": "http",
"port": 3000,
"npm-path": "/api/packages/novox/npm/",
"public-root": "https://${bound:route:name-web}/api/packages/novox/npm/"
},
"git": {
"scheme": "http",
"port": 3000
}
},
"receives": {
"npm-package-registry": "${dir:grants}/npm.json"
},
"grants": {
"npm-package-registry": "${dir:grants}"
},
"claims": [
{
"name": "npm-package-registry",
"scope": "mesh"
},
{
"name": "git",
"scope": "mesh"
}
],
"data": {
"own": [
{
"id": "data",
"path": "${dir:data}",
"class": "valuable",
"why": "every repository, its issues and attachments, and the package registry"
}
],
"consumers": {
"npm-package-registry": {
"class": "rebuildable",
"in": "data",
"why": "a consumer's packages are published again from its source"
}
}
},
"settings": {
"ready-authors": {
"kind": "preference",
"default": "mesh-tools mesh-admin jschoubben",
"why": "the forge accounts whose comment \"ready at <full head sha>\" marks a pull request ready for mesh-delivery to merge, separated by spaces: the account the forge's tools act as (novox/hq ADR 0365), the admin account for marks made before the tools acted as their own, and the operator's (novox/hq issue 722). A comment by any other account is ignored and said in the forge's log. Trusted: who may have a change merged is the operator's word"
}
},
"resources": [
{
"id": "mesh-state",
"type": "directory",
"mode": "0700",
"place": "mesh"
},
{
"id": "runtime-state",
"type": "directory",
"path": "${dir:mesh-state}/state",
"mode": "0700"
},
{
"id": "ready-authors",
"type": "file",
"path": "${dir:mesh-state}/ready-authors",
"mode": "0600",
"trusted": true,
"content": "# The accounts whose \"ready at <sha>\" comment marks a pull request ready (novox/hq issue 722), from the setting\n# ready-authors, written by the mesh. Replaced on every push; change the setting, never this file.\nauthors=${setting:ready-authors}\n"
},
{
"id": "state",
"type": "directory",
"mode": "0700",
"place": "."
},
{
"id": "grants",
"type": "directory",
"mode": "0700"
},
{
"id": "server-env",
"type": "file",
"path": "${dir:state}/server.env",
"mode": "0600",
"content": "GITEA__security__INTERNAL_TOKEN=${secret:internal-token}\nGITEA__database__DB_TYPE=postgres\nGITEA__database__HOST=${bound:postgres-database:at}:${bound:postgres-database:port}\nGITEA__database__NAME=${bound:postgres-database:as}\nGITEA__database__USER=${bound:postgres-database:as}\nGITEA__database__PASSWD=${secret:postgres-database}\nGITEA__webhook__ALLOWED_HOST_LIST=172.17.0.1\n"
},
{
"id": "data",
"type": "directory",
"mode": "0700",
"owner": "1000:1000"
},
{
"id": "server",
"type": "container",
"health": {
"kind": "http",
"endpoint": "web",
"path": "/api/healthz",
"status": 200,
"needs": "postgres-database"
},
"name": "gitea",
"image": "gitea/gitea@sha256:87a67ee09d3ae0d1df5fda5dcda3e2a1f9236a45b0a59025d6e00e46adc43bef",
"env": {
"DB_TYPE": "postgres",
"USER_UID": "1000",
"USER_GID": "1000"
},
"env-file": [
"${dir:state}/server.env"
],
"ports": [
"3000",
"222:22"
],
"volumes": [
"${dir:data}:/data"
],
"secrets-in-environment": "gitea honours GITEA__database__PASSWD__FILE and GITEA__security__INTERNAL_TOKEN__FILE; convertible, awaiting a bed that proves it",
"logging": "journald"
},
{
"id": "admin-bootstrap",
"type": "container",
"name": "mesh-gitea-admin",
"image": "gitea/gitea@sha256:87a67ee09d3ae0d1df5fda5dcda3e2a1f9236a45b0a59025d6e00e46adc43bef",
"run-once": true,
"env": {
"USER_UID": "1000",
"USER_GID": "1000",
"MESH_GITEA_ADMIN_USER": "mesh-admin"
},
"env-file": [
"${dir:state}/server.env"
],
"volumes": [
"${dir:data}:/data",
"${dir:state}/admin.secret:/run/secrets/admin:ro"
],
"args": [
"/bin/sh",
"-c",
"su-exec git gitea admin user create --admin --username \"$MESH_GITEA_ADMIN_USER\" --email mesh-admin@localhost --password \"$(cat /run/secrets/admin)\" --must-change-password=false || true"
],
"secrets-in-environment": "gitea honours GITEA__database__PASSWD__FILE and GITEA__security__INTERNAL_TOKEN__FILE; convertible, awaiting a bed that proves it"
},
{
"id": "runtime-config",
"type": "file",
"path": "${dir:mesh-state}/config.json",
"mode": "0600",
"content": "{}\n",
"merge": "json",
"takes-settings": []
}
],
"provides": [
{
"name": "npm-package-registry",
"scope": "mesh",
"identity": {
"max": 40,
"in": "a Gitea user name"
}
},
{
"name": "git",
"scope": "mesh"
}
],
"build": {
"artifacts": [
{
"name": "code",
"kind": "bundle",
"language": "typescript",
"entrypoints": [
"index.js",
"tools/index.js",
"provisioner/index.js"
],
"loads": [
"index.js",
"tools/index.js",
"provisioner/index.js"
],
"env": {
"MESH_GITEA_URL": "http://127.0.0.1:${port:3000}",
"MESH_GITEA_CONFIG_FILE": "${dir:mesh-state}/config.json",
"MESH_GITEA_ADMIN_USER": "mesh-admin",
"MESH_GITEA_ADMIN_PASSWORD_FILE": "${dir:state}/admin.secret",
"MESH_GITEA_TOOLS_USER": "mesh-tools",
"MESH_GITEA_STATE_DIR": "${dir:runtime-state}",
"MESH_RECEIVES": "${dir:grants}/npm.json",
"MESH_GITEA_READY_AUTHORS_FILE": "${dir:mesh-state}/ready-authors",
"MESH_GITEA_HOOK_URL": "http://172.17.0.1:3047",
"MESH_GITEA_HOOK_PORT": "3047",
"MESH_GITEA_HOOK_HOST": "172.17.0.1"
}
},
{
"name": "npm-registry",
"kind": "bundle",
"language": "go",
"system": "arch",
"from": "cmd/npm-registry",
"binary": "npm-registry",
"loads": [
"npm-registry"
],
"env": {
"MESH_GITEA_URL": "http://127.0.0.1:${port:3000}",
"MESH_GITEA_ADMIN_USER": "mesh-admin",
"MESH_GITEA_ADMIN_PASSWORD_FILE": "${dir:state}/admin.secret",
"MESH_NPM_OWNER": "novox"
}
},
{
"name": "forge",
"kind": "bundle",
"language": "go",
"system": "arch",
"from": "cmd/forge",
"binary": "forge",
"loads": [
"forge"
],
"env": {
"MESH_GITEA_URL": "http://127.0.0.1:${port:3000}",
"MESH_GITEA_TOOLS_USER": "mesh-tools",
"MESH_GITEA_TOKEN_FILE": "${dir:runtime-state}/token"
}
}
]
},
"jails": [
{
"name": "gitea",
"failregex": "^.*Failed authentication attempt for .* from <HOST>(?::\\d+)?\\s*$\n ^.*Invalid user .* from <HOST> port \\d+\\s*$\n ^.*User \\S+ from <HOST> not allowed because .*$",
"jail": "backend = systemd\njournalmatch = CONTAINER_NAME=gitea\nport = http,https,222\nmaxretry = 3\nfindtime = 1d\nbantime = 1d"
}
]
}