Feature 57 (config and provisioning): modules declare config and provisions, their files rendered from templates; synced with main (issues of feature 57, 874)
This commit is contained in:
+58
-5
@@ -44,7 +44,7 @@ base64-encoded. The Go bundle `cmd/forge` serves the commit, and the reads an ag
|
||||
|
||||
| tool | | what |
|
||||
|---|---|---|
|
||||
| `gitea_commit` | a | one commit on a branch of its own, created from `base` (default the default branch) when absent, carrying either `files` (each `{path, content \| content_base64 \| delete: true}`) or `patch` (a unified diff, as `git diff` writes it); answers the commit and its parent |
|
||||
| `gitea_commit` | a | one commit on a branch of its own, created from `base` (default the default branch) when absent, carrying either `files` (each `{path, content \| content_base64 \| delete: true}`) `patch` (a unified diff, as `git diff` writes it), or `patch_file` with `patch_sha256` (a patch too large to type, see below); answers the commit and its parent |
|
||||
| `gitea_commits` | r | a ref's history, newest first, optionally only commits touching a path: sha, author, date, subject |
|
||||
| `gitea_compare` | r | what head holds that base does not: its commits and the files they touch |
|
||||
| `gitea_pull_reviews` | r | a pull request's reviews — state, the commit reviewed and whether it is the head now, stale, dismissed — with their line comments |
|
||||
@@ -52,13 +52,28 @@ base64-encoded. The Go bundle `cmd/forge` serves the commit, and the reads an ag
|
||||
| `gitea_search_code` | r | the lines of a ref's text files matching a literal string or a regular expression, optionally under a path |
|
||||
|
||||
**Where a commit may go** is the rule `gitea_api` already holds for a file written (novox/hq ADR 0266):
|
||||
never the repository's default branch, never a branch a protection rule names. A rule covers a branch as the
|
||||
never the repository's default branch, never a branch a protection rule names. The tools' account may not read the
|
||||
rules themselves (the forge keeps them for a repository's administrator, novox/hq ADR 0365): for it an existing branch
|
||||
is judged by the protection the forge says it applies there, and a branch not made yet by the forge itself, which
|
||||
refuses the commit that would make a branch a rule guards. A rule covers a branch as the
|
||||
forge itself decides it (Gitea 1.27, novox/hq issue 539): a plain name covers that branch in any case; a glob
|
||||
(`*`, `**`, `?`, `[abc]`, `[!abc]`, `{a,b}`, `\` escaping) covers case-sensitively with `/` as separator; a glob
|
||||
the forge cannot compile covers its own text. `cmd/forge/rules.go` and `guard.ts` hold this one rule. The rules are read before anything is sent; rules that cannot be read refuse the
|
||||
commit. Nothing about merging changes: a merge still needs an independent review at the head and both
|
||||
required checks green, through `gitea_merge_pull_request`.
|
||||
|
||||
**A patch too large to type** (novox/hq issue 852: a feature's diff is megabytes) goes as `patch_file`, with
|
||||
`patch_sha256`. The caller writes the patch into the module's spool directory `forge-patches`
|
||||
(`/var/lib/mesh/forge-patches`, mode 0733 and owned by the machine's account, which the tools run as: anyone writes,
|
||||
nobody lists; the bundle is given its path as `MESH_GITEA_PATCH_DIR`) and names it: `patch_file` is the file's NAME,
|
||||
32 to 64 characters of `0-9a-f` that the caller chooses at random, never a path. The tool then reads that one file,
|
||||
applies it exactly as it applies `patch` (same size limit, same answer) and removes it once it has found it, whether
|
||||
the commit was made or not. A file refused before that (a bad name, a missing hash or spool directory, a name with no
|
||||
file) stays in the spool. It refuses, sending nothing, a name of another shape, a file that is missing, empty, over
|
||||
4 MiB, with more than one hard link or not a regular file (a symlink is never followed), and a file whose SHA-256 is not `patch_sha256`: the directory has no sticky bit
|
||||
(the manifest allows none), so another writer could replace a file, and the hash is how the caller knows the tool read
|
||||
the bytes it wrote. It reads nothing outside the directory.
|
||||
|
||||
**How the commit is made.** `files` become one call to the forge's multi-file contents endpoint: a path the
|
||||
ref holds is updated, one it does not is created, and an update or delete carries the blob read just
|
||||
before, so a file someone changed meanwhile is refused by the forge rather than overwritten. The blobs are read at
|
||||
@@ -66,7 +81,7 @@ the commit the branch (or, for a new branch, its base) was read at, never at its
|
||||
forge's diff endpoint, which applies it; for a small change to a large file this is far shorter than the
|
||||
file. Neither endpoint takes a commit to build on, only a branch (Gitea 1.27), so the parent answered is the
|
||||
one the forge says it made the commit on; when the branch moved between the read and the commit, the answer
|
||||
says so. One commit carries at most 200 files, 4 MiB per file and 16 MiB together, or a patch of 4 MiB. Either way the commit is the forge's own, made as the forge's admin account, so its sha is not the
|
||||
says so. One commit carries at most 200 files, 4 MiB per file and 16 MiB together, or a patch of 4 MiB. Either way the commit is the forge's own, made as the tools' account, so its sha is not the
|
||||
local commit's: the merge check judges the head on the forge. After the write the branch is read back, and a
|
||||
branch not at the commit the forge named is a failure, never a success.
|
||||
|
||||
@@ -75,8 +90,10 @@ is read once and searched in memory; a binary file is passed over, a file over 4
|
||||
`skipped`, and at most `max` matches (default 100) are answered, with `truncated` when more were found. A
|
||||
search that reaches the call's deadline stops there and answers what it found, with `stopped_at_deadline`.
|
||||
|
||||
It reaches the forge through the same HTTP client as the npm-registry bundle (`internal/forgehttp`), and one
|
||||
tool call takes at most 50 seconds.
|
||||
It reaches the forge through the same HTTP client as the npm-registry bundle (`internal/forgehttp`), as the
|
||||
tools' own account (novox/hq ADR 0365 §2, issue 469): the token the TypeScript bundle mints for `mesh-tools` and
|
||||
keeps (`MESH_GITEA_TOKEN_FILE`), sent only when its scopes line names that account, never the admin login. One tool
|
||||
call takes at most 50 seconds.
|
||||
`go test ./cmd/forge` runs it against a fake forge: the files of one commit created, updated and deleted on
|
||||
a new branch from its base; an existing branch committed on; the default branch, a protected branch and
|
||||
unreadable rules refused before anything is sent; a patch sent to the forge; a branch read back at another
|
||||
@@ -85,6 +102,42 @@ changed under the commit refused; a commit over its size limits refused; protect
|
||||
forge matches them; a code search stopped at its deadline; every malformed ask refused; and each read
|
||||
summarised.
|
||||
|
||||
## Each agent's read credential (novox/hq ADR 0365 §1, §3–§5, issue 737)
|
||||
|
||||
An agent clones and fetches the repositories it may read as a forge account of its own, `agent-<node>`. The
|
||||
module offers the provision `gitea-read`, whose credential its software issues (ADR 0305) one per holder (issue
|
||||
749): the Go bundle `cmd/agent-token` serves `gitea_agent_token`, which the vault alone asks (ADR 0349 clause 10;
|
||||
any other caller is refused and nothing is minted). For each holder the vault names that is the claude-code module
|
||||
on a machine, it
|
||||
|
||||
- makes the account `agent-<node>` with the administrator's login, when there is none: no administrator, a random
|
||||
password sent once and kept nowhere, and **in no team** — it reads public repositories until the operator adds it
|
||||
to a team that reads more (§3). An account found made an administrator, inactive or barred from logging in is
|
||||
made a plain account again;
|
||||
- deletes the account's token by the name `mesh-agent-read` (by its id, every one of them) and mints one whose one
|
||||
scope is `read:repository`: whatever team it is put in, the forge refuses it a push, a comment and any token or
|
||||
administration of its own (§5). Every ask mints anew; that is its rotation;
|
||||
- answers git's credential-store lines for it — the forge's public origin, read from `origins` (the route's name),
|
||||
and on the forge's own machine its address there too — sealed to the key the vault made for the call. A holder of
|
||||
another module is not answered and said under `skipped`.
|
||||
|
||||
The vault keeps each as `provision/<forge's node>/gitea-read/<node>/claude-code` and hands it to that holder alone;
|
||||
the claude-code module places it in the agent's home (ADR 0365 §4). The token is never logged, answered in the clear
|
||||
or put in an error. `go test ./cmd/agent-token` runs it against a fake forge.
|
||||
|
||||
## The agents' read teams (novox/hq ADR 0365 §3, issue 758)
|
||||
|
||||
An agent's account reads more than public repositories only through a team, per repository, and only to read. Which
|
||||
teams exist and who is in them is the operator's, and it is one trusted config value, `agent-read-teams`:
|
||||
`{"<team>": {"repos": ["<owner>/<repo>", ...], "members": ["<node>", ...]}}`, a member `<node>` being the account
|
||||
`agent-<node>`. The module renders it into `agent-teams.json` and runs `cmd/agent-teams` when it starts and
|
||||
whenever that file changes. For each team it makes `agent-read-<team>` in the organisation of the first repository's
|
||||
owner, with permission read and no other right, its repositories exactly the listed ones and its members exactly the
|
||||
listed accounts (an account that does not exist yet is skipped and said). A team named `agent-read-*` that has left
|
||||
the value is emptied, never deleted. A team without that prefix is never touched. There are no verbs: a change is a
|
||||
change of the value. It says each change in one line on stdout, and never a secret. `go test ./cmd/agent-teams`
|
||||
runs it against a fake forge.
|
||||
|
||||
## The package registry's retention (novox/hq ADR 0251 §4, to-be 51)
|
||||
|
||||
Every publish adds a version to the registry, and until this nothing removed one. The Go bundle
|
||||
|
||||
Reference in New Issue
Block a user