Files
mesh-catalog/modules/gitea/module.json
T
jschoubben 69ceac7fcf Join: merge A3 (wip/registry-a3)
Conflicts, both sides kept: artifact-store-tools' invokes (the assigner's
and indexer's read tools, followed through views, beside the registry
seats' hold, release and remove), its main (the views reader and the
seat asker), its manifest row; gitea's invokes (the indexer's read tool,
the hand act and the builder's versions) and its consumes of the
indexer's views. gitea's account test takes the packages account's
names; repoCheckFacts, removed on main, is not imported. No catalogue
module reads the indexer's state.

Claude-Session: https://claude.ai/code/session_01JG73KJNrH5EgFXMKKYSrig
2026-11-04 16:38:20 +01:00

626 lines
19 KiB
JSON

{
"module": "gitea",
"version": "1",
"moved": true,
"requires": [
"postgres-database",
"route",
"secret",
{
"name": "incoming",
"storage": "object-store",
"template": "objects",
"options": {
"max-mib": 2048
}
},
{
"name": "transfers",
"storage": "key-value",
"template": "state",
"options": {
"ttl-seconds": 86400
}
},
{
"name": "findings",
"storage": "key-value",
"template": "state"
}
],
"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",
"repo.writable",
"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"
],
"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 \u2014 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
},
"go-package-registry": {
"scheme": "http",
"port": 3000,
"go-path": "/api/packages/novox/go"
}
},
"claims": [
{
"name": "npm-package-registry",
"scope": "mesh",
"serves": [
"publish",
"publish-part",
"remove",
"versions"
]
},
{
"name": "go-package-registry",
"scope": "mesh",
"serves": [
"publish",
"publish-part",
"remove",
"versions"
]
},
{
"name": "git",
"scope": "mesh"
}
],
"data": {
"own": [
{
"id": "data",
"path": "${dir:data}",
"class": "valuable",
"why": "every repository, its issues and attachments, and the package registry"
}
]
},
"config": {
"url": {
"type": "string",
"description": "The address the module's tools reach the forge at, as a URL; unset, the code falls back to its environment, else this machine's gitea on its port.",
"trusted": true,
"default": ""
},
"agent-read-teams": {
"type": "object",
"description": "The read teams of the agents' forge accounts (ADR 0365 clause 3): each team by name, {\"<team>\": {\"repos\": [\"<owner>/<repo>\", ...], \"members\": [\"<node>\", ...]}}. The forge gets a team agent-read-<team> that reads only, with exactly those repositories and the accounts agent-<node>; a team taken out of the value is emptied. Nothing else on the forge is touched; an agent account is in no team until it is listed here.",
"trusted": true,
"default": {}
},
"keep-main-rules": {
"type": "boolean",
"description": "Whether the module keeps main's branch protection as the mesh wants it. True: every ten minutes, and at the start, main's rule in every repository of the organisation novox is put to no push and the tools' account alone merging, and each apply of the `repositories` value narrows the main rule of the repositories it lists, requiring no status. False (the default): the module changes no existing repository's branch protection, whoever set it at the forge, but takes away a required status it finds on a rule covering main, once (ADR 0534 \u00a71); a repository listed in `repositories` that does not exist is still made, with its first protection.",
"trusted": true,
"default": false
},
"repositories": {
"type": "object",
"description": "The repositories of the organisation novox the mesh holds (ADR 0428): each by name, {\"<name>\": {\"private\": true, \"description\": \"...\"}}. One that does not exist is made by the module's setup login under its own account, its main protected and given a first commit (a README naming the description ), its rule narrowed to no push, the tools' account and main-mergers alone merging, and no status required (ADR 0534), and only then moved into the organisation; one that exists only has main's rule put right so. Every other repository has the statuses its main requires taken off; nothing else of a repository out of the value is touched. With keep-main-rules false a listed repository that exists likewise has only its required statuses taken off, once.",
"trusted": true,
"default": {}
},
"check-gates-merge": {
"type": "boolean",
"description": "Whether gitea_merge_pull_request judges mesh/check before it merges. True (the default): a head merges only on a passing mesh/check judged against the trunk's head (ADR 0447, ADR 0534 \u00a74). False (the operator, 2026-10-25 22:56: \"run the check locally only, but not as a gate, it loses us too much time\"): a head the operator's Approve names in approved_heads merges whatever its mesh/check says (pending, failed, missing or judged on an older trunk); a head that moved, a branch no rule guards and a merge git refuses are still refused. Set together with mesh-delivery-gate's check-gates-merge.",
"trusted": true,
"default": true
},
"main-mergers": {
"type": "list",
"description": "The forge logins that may merge into main beside the tools' account, on every repository of the organisation (ADR 0534 \u00a72): the operator's own, so the operator can always merge by hand at the forge. The tools' account is always on main's merge whitelist; no agent's account belongs here.",
"trusted": true,
"default": [
"jschoubben"
]
},
"callers": {
"type": "string",
"trusted": true,
"default": "",
"description": "per verb of the two package seats, the callers that may call it (JSON object of verb to callers; * every proved caller, operator the operator's session); empty is design 51's list: publish and publish-part mesh-build-go and mesh-build-node, remove mesh-delivery-builder, versions anyone"
}
},
"facts": {
"forge-host": {
"path": "/etc/gitea/forge-host",
"template": "{{range .Machines}}{{if eq .Name $.Node}}{{.FQDN}}{{end}}{{end}}\n"
}
},
"resources": [
{
"id": "mesh-state",
"type": "directory",
"mode": "0700",
"place": "mesh",
"unknown": "program"
},
{
"id": "runtime-state",
"type": "directory",
"path": "${dir:mesh-state}/state",
"mode": "0700",
"unknown": "program"
},
{
"id": "forge-patches",
"type": "directory",
"path": "/var/lib/mesh/forge-patches",
"mode": "0733",
"owner": "${machine:account}",
"unknown": "program"
},
{
"id": "state",
"type": "directory",
"mode": "0700",
"place": ".",
"unknown": "program"
},
{
"id": "server-env",
"type": "file",
"path": "${dir:state}/server.env",
"mode": "0600",
"template": "files/server.env"
},
{
"id": "data",
"type": "directory",
"mode": "0700",
"owner": "1000:1000",
"unknown": "program"
},
{
"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",
"SSH_MAX_STARTUPS": "100:30:200"
},
"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": "origins",
"type": "file",
"path": "${dir:mesh-state}/origins",
"mode": "0644",
"template": "files/origins"
},
{
"id": "runtime-config",
"type": "file",
"path": "${dir:mesh-state}/config.json",
"mode": "0600",
"template": "files/config.json",
"merge": "json",
"takes-config": [
"url",
"keep-main-rules",
"main-mergers",
"check-gates-merge"
]
},
{
"id": "agent-teams-file",
"type": "file",
"path": "${dir:mesh-state}/agent-teams.json",
"mode": "0600",
"template": "files/agent-teams.json",
"merge": "json",
"takes-config": [
"agent-read-teams"
]
},
{
"id": "repositories-file",
"type": "file",
"path": "${dir:mesh-state}/repositories.json",
"mode": "0600",
"template": "files/repositories.json"
},
{
"id": "consumers",
"type": "file",
"path": "${dir:state}/consumers.json",
"mode": "0600",
"template": "files/consumers.json"
},
{
"id": "agent-teams",
"type": "process",
"name": "gitea-agent-teams",
"artifact": "agent-teams",
"run": [
"./agent-teams"
],
"run-once": true,
"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_AGENT_TEAMS_FILE": "${dir:mesh-state}/agent-teams.json"
},
"restart-on": [
"agent-teams-file"
]
},
{
"id": "repositories",
"type": "process",
"name": "gitea-repositories",
"artifact": "repositories",
"run": [
"./repositories",
"sync"
],
"run-once": true,
"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_GITEA_TOOLS_USER": "mesh-tools",
"MESH_REPOSITORIES_FILE": "${dir:mesh-state}/repositories.json",
"MESH_REPOSITORIES_OUTCOME": "${dir:runtime-state}/repositories-outcome.json",
"MESH_GITEA_CONFIG_FILE": "${dir:mesh-state}/config.json"
},
"restart-on": [
"repositories-file",
"runtime-config"
]
}
],
"provides": [
{
"name": "npm-package-registry",
"credential": {
"form": "none"
},
"scope": "mesh"
},
{
"name": "go-package-registry",
"credential": {
"form": "none"
},
"scope": "mesh"
},
{
"name": "git",
"credential": {
"form": "none"
},
"scope": "mesh"
},
{
"name": "gitea-read",
"scope": "mesh",
"credential": {
"issued-by": "software",
"tool": "gitea_agent_token"
}
},
{
"name": "forge-account",
"credential": {
"form": "none"
},
"scope": "mesh",
"answered-by": "helper"
}
],
"build": {
"artifacts": [
{
"name": "code",
"kind": "bundle",
"language": "typescript",
"entrypoints": [
"index.js",
"tools/index.js"
],
"loads": [
"index.js",
"tools/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_GITEA_HOOK_URL": "http://172.17.0.1:3047",
"MESH_GITEA_HOOK_PORT": "3047",
"MESH_GITEA_HOOK_HOST": "172.17.0.1"
}
},
{
"name": "package-registry",
"kind": "bundle",
"language": "go",
"system": "arch",
"from": "cmd/package-registry",
"binary": "package-registry",
"loads": [
"package-registry"
],
"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",
"MESH_GITEA_PACKAGES_USER": "mesh-packages",
"MESH_GITEA_PACKAGES_TOKEN_FILE": "${dir:runtime-state}/packages-token",
"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",
"MESH_GITEA_PATCH_DIR": "${dir:forge-patches}"
}
},
{
"name": "files",
"kind": "archive",
"from": "files"
},
{
"name": "agent-token",
"kind": "bundle",
"language": "go",
"system": "arch",
"from": "cmd/agent-token",
"binary": "agent-token",
"loads": [
"agent-token"
],
"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_GITEA_ORIGINS_FILE": "${dir:mesh-state}/origins"
}
},
{
"name": "agent-teams",
"kind": "bundle",
"language": "go",
"system": "arch",
"from": "cmd/agent-teams",
"binary": "agent-teams",
"loads": [
"agent-teams"
]
},
{
"name": "repositories",
"kind": "bundle",
"language": "go",
"system": "arch",
"from": "cmd/repositories",
"binary": "repositories",
"loads": [
"repositories"
]
},
{
"name": "forge-account-provider",
"kind": "bundle",
"language": "go",
"system": "arch",
"from": "cmd/forge-account-provider",
"binary": "forge-account-provider",
"loads": [
"forge-account-provider"
],
"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_GITEA_ORIGINS_FILE": "${dir:mesh-state}/origins",
"MESH_GITEA_SSH_PORT": "222",
"MESH_GITEA_HOST_KEY_FILE": "${dir:data}/ssh/ssh_host_ed25519_key.pub",
"MESH_FORGE_HOST_FILE": "/etc/gitea/forge-host"
}
}
]
},
"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"
}
],
"state": [
"bound",
"announced",
"findings"
],
"reads": [
"mesh-provisioner.plans",
"mesh-provisioner.provisions",
"ssh-client.keys",
"mesh-build-go.keys",
"mesh-build-node.keys",
"mesh-build-image.keys",
"mesh-delivery-checker.owed",
"mesh-delivery-gate.source"
],
"invokes": [
"mesh-delivery-indexer.indexer_read_modules",
"seat:mesh-hand-acts.hand-act",
"mesh-delivery-builder.builder_versions"
],
"consumes": [
"mesh-delivery-indexer.views.modules"
]
}