docs(slice1): runbook section 1 run through the Gitea admin API (row 35, #1517, lead decision 74)
Jason ruled that agents run the steps his admin grant to the jarvis Gitea token covers. Sage created the four mosaic-stack bots (ids 114-117, restricted, non-admin), added them as collaborators (W/W/W/R), and minted one scoped token each (ids 191-194). The tokens were written 0600 outside the repo. Scripts and receipt are in agents/sage/work/gitea-setup/. The guide and SR brief now say who runs which section. Sections 2 to 4 (Vikunja) stay with Jason. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
@@ -3766,3 +3766,13 @@ Follow-ups, none blocking:
|
|||||||
- A refusal line with a future `at` holds the DM silently. A clock step back lengthens the wait (Darkwing, Filbert). Both fail safe.
|
- A refusal line with a future `at` holds the DM silently. A clock step back lengthens the wait (Darkwing, Filbert). Both fail safe.
|
||||||
- The README calls 429 "unknown" while the journal records it as refused (Filbert).
|
- The README calls 429 "unknown" while the journal records it as refused (Filbert).
|
||||||
- A regular file at the directory path reports "mode 0700" where "not a directory" would be clearer (Darkwing).
|
- A regular file at the directory path reports "mode 0700" where "not a directory" would be clearer (Darkwing).
|
||||||
|
|
||||||
|
### 2026-10-09 — Sage, slice 1 runbook section 1: Gitea bots through the admin API (row 35, #1517, lead decision 74)
|
||||||
|
|
||||||
|
Before: no `mosaic-stack-*-bot` users (all four returned 404). `~/.config/mosaic-dev/secrets/mosaic-stack/` existed at 0700 and was empty, since Jason had run section 0 at 22:13 UTC. Row 35 waited on Jason to run the whole runbook by hand.
|
||||||
|
|
||||||
|
Jason ruled that agents run the steps his admin grant to the jarvis Gitea token covers. Sage ran section 1 with a Node script outside the repo, now kept with its receipt in `agents/sage/work/gitea-setup/`. Nothing secret reached argv, stdout or a URL. Each bot's password was random, held in memory and discarded after it minted that bot's token.
|
||||||
|
|
||||||
|
After: users ids 114 to 117, none admin, all `restricted` with `private` visibility. Collaborators Write for pm, cto and coder, Read for reviewer. Tokens ids 191 to 194 with the guide's scopes, four files at 0600 and 40 bytes. `verify.mjs` showed each token logs in as its bot. Reviewer has push=false. Every token gets 403 on `admin/users` and on the org listing. The main and next allowlists stayed empty. A `prohibit_login` toggle on the pm bot gave 403 on every route and then restored it.
|
||||||
|
|
||||||
|
Not done: sections 2 to 4 (Vikunja). They need the estate instance (T236) and the owner and `svc-mosaic-stack` logins, and no agent holds those. Follow-up due before 2027-01-07: the script deletes the old token during a rotation. No suite covers this work. It touches no code the suites run.
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
Runbook section 1 (docs/guides/slice-1-identities.md), run by Sage with the
|
||||||
|
jarvis admin token on git.mosaicstack.dev (Gitea 1.27.1), 2026-10-09, between 22:21 and 22:23 UTC.
|
||||||
|
Lead decision 74. Output below is verbatim; it holds names, ids and statuses only.
|
||||||
|
|
||||||
|
$ node setup.mjs pm
|
||||||
|
mosaic-stack-pm-bot: create HTTP 201 id=114 admin=false restricted=true
|
||||||
|
mosaic-stack-pm-bot: collaborator write HTTP 204
|
||||||
|
mosaic-stack-pm-bot: token "mosaic-stack-pm-2026-10-09" HTTP 201 id=191 scopes=write:issue,read:repository,read:user
|
||||||
|
|
||||||
|
$ node setup.mjs cto coder reviewer
|
||||||
|
mosaic-stack-cto-bot: create HTTP 201 id=115 admin=false restricted=true
|
||||||
|
mosaic-stack-cto-bot: collaborator write HTTP 204
|
||||||
|
mosaic-stack-cto-bot: token "mosaic-stack-cto-2026-10-09" HTTP 201 id=192 scopes=write:issue,write:repository,read:user
|
||||||
|
mosaic-stack-coder-bot: create HTTP 201 id=116 admin=false restricted=true
|
||||||
|
mosaic-stack-coder-bot: collaborator write HTTP 204
|
||||||
|
mosaic-stack-coder-bot: token "mosaic-stack-coder-2026-10-09" HTTP 201 id=193 scopes=write:issue,write:repository,read:user
|
||||||
|
mosaic-stack-reviewer-bot: create HTTP 201 id=117 admin=false restricted=true
|
||||||
|
mosaic-stack-reviewer-bot: collaborator read HTTP 204
|
||||||
|
mosaic-stack-reviewer-bot: token "mosaic-stack-reviewer-2026-10-09" HTTP 201 id=194 scopes=write:issue,write:repository,read:user
|
||||||
|
|
||||||
|
$ node verify.mjs
|
||||||
|
pm: user=200 login=mosaic-stack-pm-bot admin=false | repo=200 push=true admin=false pull=true | issues=200 | admin/users=403 | org repos=403 n=-
|
||||||
|
cto: user=200 login=mosaic-stack-cto-bot admin=false | repo=200 push=true admin=false pull=true | issues=200 | admin/users=403 | org repos=403 n=-
|
||||||
|
coder: user=200 login=mosaic-stack-coder-bot admin=false | repo=200 push=true admin=false pull=true | issues=200 | admin/users=403 | org repos=403 n=-
|
||||||
|
reviewer: user=200 login=mosaic-stack-reviewer-bot admin=false | repo=200 push=false admin=false pull=true | issues=200 | admin/users=403 | org repos=403 n=-
|
||||||
|
|
||||||
|
$ stat -c '%a %s %n' ~/.config/mosaic-dev/secrets/mosaic-stack/*
|
||||||
|
600 40 .../mosaic-stack/coder-gitea.token
|
||||||
|
600 40 .../mosaic-stack/cto-gitea.token
|
||||||
|
600 40 .../mosaic-stack/pm-gitea.token
|
||||||
|
600 40 .../mosaic-stack/reviewer-gitea.token
|
||||||
|
|
||||||
|
Branch protections on mosaicstack/stack before and after: main and next,
|
||||||
|
push, merge and approvals allowlists all empty. No bot is on any.
|
||||||
|
|
||||||
|
Revocation check, pm bot, about 22:25 UTC: PATCH admin/users prohibit_login=true
|
||||||
|
(HTTP 200), then verify.mjs pm gave user=403 repo=403 issues=403. PATCH
|
||||||
|
prohibit_login=false (HTTP 200, restricted=true admin=false), then verify.mjs pm
|
||||||
|
gave user=200 repo=200 push=true issues=200 admin/users=403. prohibit_login
|
||||||
|
stops a bot's tokens at once, and clearing it restores them.
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
// Runbook section 1 (docs/guides/slice-1-identities.md) via the admin API.
|
||||||
|
// Prints names, ids and HTTP statuses only. No secret reaches argv, stdout or a URL.
|
||||||
|
import fs from "node:fs";
|
||||||
|
import crypto from "node:crypto";
|
||||||
|
import os from "node:os";
|
||||||
|
|
||||||
|
const BASE = "https://git.mosaicstack.dev/api/v1";
|
||||||
|
const REPO = "mosaicstack/stack";
|
||||||
|
const S = `${os.homedir()}/.config/mosaic-dev/secrets/mosaic-stack`;
|
||||||
|
const DATE = new Date().toISOString().slice(0, 10);
|
||||||
|
const ADMIN_FILE = `${os.homedir()}/.mosaic/fleet/agents/jarvis/secrets/gitea-mosaicstack-jarvis.token`;
|
||||||
|
|
||||||
|
const st = fs.lstatSync(ADMIN_FILE);
|
||||||
|
if (!st.isFile() || (st.mode & 0o077) !== 0) throw new Error("admin token file refused");
|
||||||
|
const adminTok = fs.readFileSync(ADMIN_FILE, "utf8").trim();
|
||||||
|
if (!/^[0-9a-f]{40}$/.test(adminTok)) throw new Error("admin token shape refused");
|
||||||
|
const sd = fs.lstatSync(S);
|
||||||
|
if (!sd.isDirectory() || (sd.mode & 0o077) !== 0) throw new Error("secrets dir refused");
|
||||||
|
|
||||||
|
const ROLES = [
|
||||||
|
{ r: "pm", perm: "write", scopes: ["write:issue", "read:repository", "read:user"] },
|
||||||
|
{ r: "cto", perm: "write", scopes: ["write:issue", "write:repository", "read:user"] },
|
||||||
|
{ r: "coder", perm: "write", scopes: ["write:issue", "write:repository", "read:user"] },
|
||||||
|
{ r: "reviewer", perm: "read", scopes: ["write:issue", "write:repository", "read:user"] },
|
||||||
|
];
|
||||||
|
|
||||||
|
async function call(method, path, body, auth = `token ${adminTok}`) {
|
||||||
|
const res = await fetch(`${BASE}/${path}`, {
|
||||||
|
method,
|
||||||
|
headers: { Authorization: auth, "Content-Type": "application/json", Accept: "application/json" },
|
||||||
|
body: body === undefined ? undefined : JSON.stringify(body),
|
||||||
|
});
|
||||||
|
const text = await res.text();
|
||||||
|
let json = null;
|
||||||
|
try { json = text ? JSON.parse(text) : null; } catch {}
|
||||||
|
return { status: res.status, json };
|
||||||
|
}
|
||||||
|
|
||||||
|
const only = process.argv.slice(2);
|
||||||
|
for (const { r, perm, scopes } of ROLES) {
|
||||||
|
if (only.length && !only.includes(r)) continue;
|
||||||
|
const u = `mosaic-stack-${r}-bot`;
|
||||||
|
const file = `${S}/${r}-gitea.token`;
|
||||||
|
if (fs.existsSync(file)) { console.log(`${u}: token file exists, skipped`); continue; }
|
||||||
|
|
||||||
|
let pw = crypto.randomBytes(36).toString("base64url");
|
||||||
|
const got = await call("GET", `users/${u}`);
|
||||||
|
let res;
|
||||||
|
if (got.status === 404) {
|
||||||
|
res = await call("POST", "admin/users", {
|
||||||
|
username: u, full_name: `mosaic-stack ${r} bot`, email: `${u}@noreply.mosaicstack.dev`,
|
||||||
|
password: pw, must_change_password: false, send_notify: false,
|
||||||
|
restricted: true, visibility: "private",
|
||||||
|
});
|
||||||
|
console.log(`${u}: create HTTP ${res.status} id=${res.json?.id} admin=${res.json?.is_admin} restricted=${res.json?.restricted}`);
|
||||||
|
if (res.status !== 201) { console.log(` message: ${res.json?.message}`); process.exit(1); }
|
||||||
|
} else if (got.status === 200) {
|
||||||
|
res = await call("PATCH", `admin/users/${u}`, { login_name: u, source_id: 0, password: pw, must_change_password: false });
|
||||||
|
console.log(`${u}: exists id=${got.json?.id}, password reset HTTP ${res.status}`);
|
||||||
|
if (res.status !== 200) process.exit(1);
|
||||||
|
} else { console.log(`${u}: lookup HTTP ${got.status}`); process.exit(1); }
|
||||||
|
|
||||||
|
res = await call("PUT", `repos/${REPO}/collaborators/${u}`, { permission: perm });
|
||||||
|
console.log(`${u}: collaborator ${perm} HTTP ${res.status}`);
|
||||||
|
if (res.status !== 204) process.exit(1);
|
||||||
|
|
||||||
|
const basic = "Basic " + Buffer.from(`${u}:${pw}`).toString("base64");
|
||||||
|
pw = null;
|
||||||
|
res = await call("POST", `users/${u}/tokens`, { name: `mosaic-stack-${r}-${DATE}`, scopes }, basic);
|
||||||
|
const tok = res.json?.sha1;
|
||||||
|
console.log(`${u}: token "mosaic-stack-${r}-${DATE}" HTTP ${res.status} id=${res.json?.id} scopes=${(res.json?.scopes || []).join(",")}`);
|
||||||
|
if (res.status !== 201 || typeof tok !== "string" || tok.length === 0) { console.log(` message: ${res.json?.message}`); process.exit(1); }
|
||||||
|
fs.writeFileSync(file, tok, { flag: "wx", mode: 0o600 });
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
// Probe each bot token: identity, repo permission, and refusals. Prints statuses only.
|
||||||
|
import fs from "node:fs";
|
||||||
|
import os from "node:os";
|
||||||
|
|
||||||
|
const BASE = "https://git.mosaicstack.dev/api/v1";
|
||||||
|
const S = `${os.homedir()}/.config/mosaic-dev/secrets/mosaic-stack`;
|
||||||
|
const roles = process.argv.slice(2).length ? process.argv.slice(2) : ["pm", "cto", "coder", "reviewer"];
|
||||||
|
|
||||||
|
for (const r of roles) {
|
||||||
|
const tok = fs.readFileSync(`${S}/${r}-gitea.token`, "utf8");
|
||||||
|
const h = { Authorization: `token ${tok}`, Accept: "application/json" };
|
||||||
|
const get = async (p) => { const res = await fetch(`${BASE}/${p}`, { headers: h }); let j = null; try { j = await res.json(); } catch {} return { s: res.status, j }; };
|
||||||
|
const me = await get("user");
|
||||||
|
const repo = await get("repos/mosaicstack/stack");
|
||||||
|
const issue = await get("repos/mosaicstack/stack/issues?limit=1");
|
||||||
|
const admin = await get("admin/users?limit=1");
|
||||||
|
const org = await get("orgs/mosaicstack/repos?limit=50");
|
||||||
|
const p = repo.j?.permissions || {};
|
||||||
|
console.log(`${r}: user=${me.s} login=${me.j?.login} admin=${me.j?.is_admin} | repo=${repo.s} push=${p.push} admin=${p.admin} pull=${p.pull} | issues=${issue.s} | admin/users=${admin.s} | org repos=${org.s} n=${Array.isArray(org.j) ? org.j.length : "-"}`);
|
||||||
|
}
|
||||||
@@ -540,3 +540,4 @@ are never rewritten or removed; corrections are new entries.
|
|||||||
2026-10-09T14:09:53Z | Rocko (T3 Claude Code, thread b84bb264) | row 45 (#1527) S4 follow-up round 2 candidate | Packet agents/rocko/work/s4-follow-up/ on d539d8d2: build.patch c8cec070…6756, candidate manifest 5b067a9d…0d0e, packet manifest 4ec1ea9a…2d30. R1 send callbacks at host.mjs:144/150/184/188 with deterministic EPIPE tests; R2 30-min refusal wait from the journal (decision 73); README manual recovery; X9, X14, notes 1 and 3; append type-check declined with reason. Gate green except test-task's 2 Docker cases (conversation 152/0); 31/31 mutants killed. Queue revs 212-214 uncommitted: round 2 opened, request rocko-r45-review-r2 failed (no credential), for Sage to post. No commits, pushes, Gitea calls, token or binding reads.
|
2026-10-09T14:09:53Z | Rocko (T3 Claude Code, thread b84bb264) | row 45 (#1527) S4 follow-up round 2 candidate | Packet agents/rocko/work/s4-follow-up/ on d539d8d2: build.patch c8cec070…6756, candidate manifest 5b067a9d…0d0e, packet manifest 4ec1ea9a…2d30. R1 send callbacks at host.mjs:144/150/184/188 with deterministic EPIPE tests; R2 30-min refusal wait from the journal (decision 73); README manual recovery; X9, X14, notes 1 and 3; append type-check declined with reason. Gate green except test-task's 2 Docker cases (conversation 152/0); 31/31 mutants killed. Queue revs 212-214 uncommitted: round 2 opened, request rocko-r45-review-r2 failed (no credential), for Sage to post. No commits, pushes, Gitea calls, token or binding reads.
|
||||||
2026-10-09T14:26Z | Filbert (T3 Claude Code, thread 9cb9731e) | row 45 (#1527) S4 follow-up review round 2 | approve, comment 26886, queue rev 218 (5eb9fa8a), record 9b067be1; B1 resolved (permanent 403 sends at 0/30/60/90/120 min, gave-up at 120, also under a 15 s crash loop and a 401), R1 resolved (4 callbacks, each mutant fails an EPIPE test), G184 fails in 22 s (CLOSE_TIMEOUT_MS) not a hang, append type-check decline agreed (A3: 20 real DMs in 10 min), 31/31 of Rocko's mutants killed, gate green but test-task 26/2 (Docker recall), notes: future-at silent hold, README 429 wording, digest-refusal pacing untested
|
2026-10-09T14:26Z | Filbert (T3 Claude Code, thread 9cb9731e) | row 45 (#1527) S4 follow-up review round 2 | approve, comment 26886, queue rev 218 (5eb9fa8a), record 9b067be1; B1 resolved (permanent 403 sends at 0/30/60/90/120 min, gave-up at 120, also under a 15 s crash loop and a 401), R1 resolved (4 callbacks, each mutant fails an EPIPE test), G184 fails in 22 s (CLOSE_TIMEOUT_MS) not a hang, append type-check decline agreed (A3: 20 real DMs in 10 min), 31/31 of Rocko's mutants killed, gate green but test-task 26/2 (Docker recall), notes: future-at silent hold, README 429 wording, digest-refusal pacing untested
|
||||||
2026-10-09T14:29:13Z | Sage (T3 Claude Code, thread 1ef1e4f8) | land row 45 (#1527) | Darkwing (26884) and Filbert (26886) approve round 2, candidate 5b067a9d. Gate on b13fef4c plus the patch all green, no COMPOSE_PROJECT_NAME override, test-task 98/0. Candidate 9cdb6d82, my trackers-boot copy removed 2be51cf5, Rocko's BUILD-LOG entries and SESSIONS line plus my landing entry 5c680166, queue rev 219 f345ae85, pushed; #1527 closed (comment 26892). Reviewers' non-blocking notes recorded for a follow-up row. Gate worktree removed; Rocko told to remove their tree.
|
2026-10-09T14:29:13Z | Sage (T3 Claude Code, thread 1ef1e4f8) | land row 45 (#1527) | Darkwing (26884) and Filbert (26886) approve round 2, candidate 5b067a9d. Gate on b13fef4c plus the patch all green, no COMPOSE_PROJECT_NAME override, test-task 98/0. Candidate 9cdb6d82, my trackers-boot copy removed 2be51cf5, Rocko's BUILD-LOG entries and SESSIONS line plus my landing entry 5c680166, queue rev 219 f345ae85, pushed; #1527 closed (comment 26892). Reviewers' non-blocking notes recorded for a follow-up row. Gate worktree removed; Rocko told to remove their tree.
|
||||||
|
2026-10-09T22:24:54Z | Sage (T3 Claude Code, thread 1ef1e4f8) | row 35 (#1517) runbook section 1, Gitea bots, on Jason's admin-token grant | lead decision 74; four mosaic-stack bots ids 114-117 restricted and non-admin, collaborators W/W/W/R, tokens 191-194 at 0600 in ~/.config/mosaic-dev/secrets/mosaic-stack (stat only), verify.mjs and a prohibit_login revocation test pass, protections unchanged; guide and SR brief updated; row 35 note and gate, revs 221-222; Vikunja half still Jason's
|
||||||
|
|||||||
@@ -1,10 +1,17 @@
|
|||||||
# Slice 1 identities: Gitea bots, Vikunja bots and their tokens
|
# Slice 1 identities: Gitea bots, Vikunja bots and their tokens
|
||||||
|
|
||||||
Jason runs this guide once per business, by hand (PRD REQ-CRED-1, round
|
An operator runs this guide once per business (PRD REQ-CRED-1, round
|
||||||
3, 1A and 2A). It creates every service identity slice 1 uses and writes
|
3, 1A and 2A). It creates every service identity slice 1 uses and writes
|
||||||
each token to a 0600 file the broker reads. Nothing in v1 mints these
|
each token to a 0600 file the broker reads. Nothing in v1 mints these
|
||||||
tokens for you. Brief: `docs/plans/2026-10-04_slice-1.md`, row SR.
|
tokens for you. Brief: `docs/plans/2026-10-04_slice-1.md`, row SR.
|
||||||
|
|
||||||
|
Who the operator is depends on the credential. On 2026-10-09 Jason gave
|
||||||
|
the jarvis Gitea token site admin rights and ruled that agents run the
|
||||||
|
steps it covers, so Sage ran section 1 for `mosaic-stack` through the
|
||||||
|
API (lead decision 74). Sections 2 to 4 need the Vikunja owner and
|
||||||
|
`svc-$BIZ` logins, which no agent holds, so they stay with Jason until he
|
||||||
|
grants a Vikunja credential.
|
||||||
|
|
||||||
Plan on about 20 minutes with an existing Vikunja, and 30 if you start
|
Plan on about 20 minutes with an existing Vikunja, and 30 if you start
|
||||||
the bundled one.
|
the bundled one.
|
||||||
|
|
||||||
@@ -18,7 +25,9 @@ the bundled one.
|
|||||||
- To check a token file, use `stat`, never `cat`.
|
- To check a token file, use `stat`, never `cat`.
|
||||||
- The Vikunja owner and `svc-$BIZ` passwords and the Gitea admin login
|
- The Vikunja owner and `svc-$BIZ` passwords and the Gitea admin login
|
||||||
are the high-value secrets. They are used only in this guide, and never reach
|
are the high-value secrets. They are used only in this guide, and never reach
|
||||||
the broker or an agent.
|
the broker or a worker. The one exception is the jarvis Gitea admin
|
||||||
|
token, which Jason granted to the lead seat for section 1 (decision 74).
|
||||||
|
It stays in its fleet file, and the broker never reads it.
|
||||||
|
|
||||||
## 0. Set up the shell
|
## 0. Set up the shell
|
||||||
|
|
||||||
@@ -40,8 +49,21 @@ The commands need curl 7.76 or later, for `--fail-with-body`.
|
|||||||
|
|
||||||
## 1. Gitea: four bot users and their tokens
|
## 1. Gitea: four bot users and their tokens
|
||||||
|
|
||||||
Gitea tokens don't expire, and the HTTP route for creating one needs a
|
Gitea tokens don't expire, and the HTTP route for creating one needs
|
||||||
password, so this part uses the web UI.
|
the bot's password. The steps below use the web UI.
|
||||||
|
|
||||||
|
With a site admin token there's an API route that does the same five
|
||||||
|
steps: `agents/sage/work/gitea-setup/setup.mjs`, which ran for
|
||||||
|
`mosaic-stack` on 2026-10-09 (receipt `2026-10-09_run.txt` beside it). It
|
||||||
|
creates each bot with a random password that exists only in its
|
||||||
|
memory, adds the collaborator, mints the token with basic auth as the bot,
|
||||||
|
and writes the file with `O_EXCL` at 0600. The password is never
|
||||||
|
written down, so nobody can log in as a bot. It also creates each bot as
|
||||||
|
`restricted` with `private` visibility, which the steps below don't ask
|
||||||
|
for. A restricted user sees only repositories it collaborates on. The
|
||||||
|
bots' addresses are `<user>@noreply.mosaicstack.dev`, which receive no
|
||||||
|
mail. `verify.mjs` checks each token's login, repository permission and
|
||||||
|
refusals, and prints no secret.
|
||||||
|
|
||||||
1. As a site admin, create the users `mosaic-stack-pm-bot`,
|
1. As a site admin, create the users `mosaic-stack-pm-bot`,
|
||||||
`mosaic-stack-cto-bot`, `mosaic-stack-coder-bot` and
|
`mosaic-stack-cto-bot`, `mosaic-stack-coder-bot` and
|
||||||
@@ -369,8 +391,17 @@ role. The stack never writes this file.
|
|||||||
the broker, then delete the old token in the same settings page.
|
the broker, then delete the old token in the same settings page.
|
||||||
Update `rotateBy`. Each rotation gets one line in
|
Update `rotateBy`. Each rotation gets one line in
|
||||||
`docs/SESSIONS.md`: date, who, which identities, and no values.
|
`docs/SESSIONS.md`: date, who, which identities, and no values.
|
||||||
|
The `mosaic-stack` bots from `setup.mjs` have no known password, so
|
||||||
|
nobody can log in to rotate. Rerunning the script for one role after
|
||||||
|
moving its old file aside resets the password and mints a new token. It
|
||||||
|
doesn't yet delete the old token, which needs the same basic auth.
|
||||||
|
Adding that is a follow-up due before the first `rotateBy`,
|
||||||
|
2027-01-07.
|
||||||
- **Revoking a role at once:** delete its token, the Gitea one in the
|
- **Revoking a role at once:** delete its token, the Gitea one in the
|
||||||
bot's settings and the Vikunja one with the `DELETE` above as
|
bot's settings and the Vikunja one with the `DELETE` above as
|
||||||
`svc-$BIZ`, or remove the bot's collaborator access or, as the owner,
|
`svc-$BIZ`, or remove the bot's collaborator access or, as the owner,
|
||||||
its project share. The broker's next
|
its project share. For a bot with no known password, a Gitea admin
|
||||||
|
removes the collaborator
|
||||||
|
(`DELETE /repos/{owner}/{repo}/collaborators/{user}`) or sets
|
||||||
|
`prohibit_login` on the user, which also stops its tokens. The broker's next
|
||||||
call gets a 401 or 403 and refuses.
|
call gets a 401 or 403 and refuses.
|
||||||
|
|||||||
@@ -1464,3 +1464,46 @@ which stay with him. Each item names who decided it and what happened.
|
|||||||
before the signal and so never hit the start-up race. Row 46
|
before the signal and so never hit the start-up race. Row 46
|
||||||
landed the fixture fix as 2d308abd. The BUILD-LOG landing entry
|
landed the fixture fix as 2d308abd. The BUILD-LOG landing entry
|
||||||
records it.
|
records it.
|
||||||
|
74. **Agents run the Gitea steps Jason's admin token covers, and
|
||||||
|
section 1 is done (2026-10-09).** Source: Jason in Sage's thread,
|
||||||
|
2026-10-09, after I asked him to run section 1: "I should not be
|
||||||
|
doing this. I have granted agentic tokens to admin the gitea server
|
||||||
|
at git.mosaicstack.dev ... I would like you to use the gitea token to
|
||||||
|
set things up. Ask me any questions for the setup, but I should not
|
||||||
|
be performing the steps I already granted rights for."
|
||||||
|
- The rule: a step a granted credential covers is an agent's step,
|
||||||
|
not Jason's. The lead seat runs it and records it. A step that
|
||||||
|
needs a credential no agent holds still goes to Jason, and so do
|
||||||
|
decisions about granting one. The grant covers the jarvis token's
|
||||||
|
admin rights on git.mosaicstack.dev only. It gives no push to
|
||||||
|
`next` or `main`, no merge and no branch-protection change, and
|
||||||
|
R10 and the merge plan's gates stand.
|
||||||
|
- Sage ran runbook section 1 for `mosaic-stack` through the API
|
||||||
|
between 22:21 and 22:23 UTC. Users `mosaic-stack-{pm,cto,coder,reviewer}-bot`
|
||||||
|
are ids 114 to 117, none admin. Collaborators on
|
||||||
|
`mosaicstack/stack` are Write for pm, cto and coder and Read for
|
||||||
|
reviewer. Tokens are ids 191 to 194, named
|
||||||
|
`mosaic-stack-<role>-2026-10-09` with the guide's scopes, in
|
||||||
|
`~/.config/mosaic-dev/secrets/mosaic-stack/<role>-gitea.token`
|
||||||
|
at 0600, 40 bytes each, checked with `stat`. The `rotateBy` base
|
||||||
|
is 2026-10-09, so the first rotation is due 2027-01-07. The main
|
||||||
|
and next protections' push, merge and approvals allowlists are
|
||||||
|
empty before and after. Receipt and scripts:
|
||||||
|
`agents/sage/work/gitea-setup/`.
|
||||||
|
- Choices the guide didn't make, all narrower than it: each bot is
|
||||||
|
`restricted` with `private` visibility. Bot passwords were random,
|
||||||
|
held in memory only and discarded, so nobody can log in as a bot.
|
||||||
|
Addresses are `<user>@noreply.mosaicstack.dev`. Jason invited
|
||||||
|
questions on these. Each had a narrower option, so I took it
|
||||||
|
instead of asking, and he can reverse any of them.
|
||||||
|
- A test on the pm bot showed `prohibit_login` stops its token at
|
||||||
|
once (403) and clearing it restores the token. With no bot
|
||||||
|
password, that flag and collaborator removal are the immediate
|
||||||
|
revocations. Follow-up before 2027-01-07: the setup script also
|
||||||
|
deletes the old token during a rotation.
|
||||||
|
- Not covered: the Vikunja half. Sections 2 to 4 need the estate
|
||||||
|
instance (T236, not serving) and the owner and `svc-mosaic-stack`
|
||||||
|
logins. No agent holds a Vikunja admin credential, so R5 keeps
|
||||||
|
`svc-mosaic-stack` with Jason until he grants one. Row 35 stays
|
||||||
|
waiting on Jason for that half only. Its note said Path B, which
|
||||||
|
decisions 66 and 67 replaced, and the note now says so.
|
||||||
|
|||||||
@@ -195,14 +195,20 @@ No token value appears in the runbook, a command line or a URL.
|
|||||||
|
|
||||||
### Out of scope
|
### Out of scope
|
||||||
|
|
||||||
- Any script that creates users or tokens. Minting is out of v1.
|
- Any script in the product that creates users or tokens. Minting is
|
||||||
- Running the runbook. Jason does that, and row S3's live tests wait for
|
out of v1. Sage's operator script for section 1
|
||||||
it.
|
(`agents/sage/work/gitea-setup/`, decision 74) isn't part of the
|
||||||
|
stack, and nothing in the stack calls it.
|
||||||
|
- Running the runbook. Sage ran section 1 (Gitea) on 2026-10-09 with
|
||||||
|
the admin token Jason granted (decision 74). Jason runs sections 2 to
|
||||||
|
4 (Vikunja) until he grants a Vikunja credential, and row S3's live
|
||||||
|
tests wait for them.
|
||||||
|
|
||||||
### Gate
|
### Gate
|
||||||
|
|
||||||
Darkwing approves the scope tables. Jason runs it, and the broker's
|
Darkwing approves the scope tables. The runbook runs, section 1 by
|
||||||
startup probe passes for every identity.
|
Sage and sections 2 to 4 by Jason, and the broker's startup probe passes
|
||||||
|
for every identity.
|
||||||
|
|
||||||
## Slice 1 S1: roles v2, business and project files, variable layers
|
## Slice 1 S1: roles v2, business and project files, variable layers
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user