ADR: the forgejo-mcp Service's shape and credential-less endpoint (#21) #43
No reviewers
Labels
No labels
needs-info
needs-triage
ready-for-agent
ready-for-human
wontfix
needs-info
needs-triage
ready-for-agent
ready-for-human
review/merge-ready
review/needs-fix
review/needs-human
review/needs-review
wayfinder:grilling
wayfinder:map
wayfinder:prototype
wayfinder:research
wayfinder:task
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
olympus/infra-forge!43
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "hermes/21-forgejo-mcp-adr"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
The decision record that gates the forgejo-mcp work: the Service's shape and its endpoint's identity. Documentation-only; no role or Stack changes.
Fixes, in prose, the choices everything after #21 depends on:
forgejoGuest, separate from theforgejorole, with its own play;/mcp(not SSE, not stdio);passthroughmode, operator-token fallback deliberately off — the Service holds no Forgejo credential;Each rejected alternative is recorded once: the signed OCI image, SSE/stdio, a configured Service token, and the operator-token fallback.
Evidence
Numbering: next free number taken at write time —
docs/adr/held 0001 and 0002, so this lands at 0003, with the index row added. The publish manifest publishesdocs/adr/*as a glob, so no manifest edit is needed.docs/adr/index.mdlisted 0001–0002.docs/adr/index.mdlists 0001–0003;make fmtexits 0; every relative link in the changed docs resolves in-repo.Grounding checked against the upstream project and the parent spec, not restated from memory:
refuses to starton a non-loopback bind with no allowlist;403on an undeclaredHost),passthrough401, and the--allow-operator-token-fallbackwarning all read back from upstream's v3.2.0 README.bf8f744d…7039df) was verified by downloadingforgejo-mcp_3.2.0_linux_amd64.tar.gzand hashing it — it matches the literal pinned in the parent spec.Merge Danger
Door: two-way
Docs-only; reverting is a
git revertof one file plus one table row. The record takes the next free ADR number rather than a reserved one, so a conflicting ADR landing first would want a renumber before merge; the index row and the filename are the only places the number appears.Blast Radius: documentation
The rationale is not inert — #22 (role), #23 (Agent round-trip) and #24 (token scope) are written against it, so a wrong line here propagates. Reviewing the why matters more than the wording.
4d8e53c77dtoeefe98c378Standards review — PR #43
Axis: does the diff follow this repo's documented coding standards?
Diff:
git diff main...origin/hermes/21-forgejo-mcp-adr(commits632dff0,eefe98c; docs-only: newdocs/adr/0003-forgejo-mcp-service-shape-and-endpoint.md+ one row indocs/adr/index.md).Standards sources:
AGENTS.md,docs/index.md,docs/adr/index.md,GLOSSARY.md+docs/agents/domain.md, plus the Fowler smell baseline.Hard violations
Vocabulary/glossary contradiction — documented standard.
GLOSSARY.md:69-72defines Service as a closed list: "Forgejo and the Actions runner are the Services on theforgejoGuest" (_Avoid_: app, daemon, process). The ADR names a "third Service" (l.1, l.9) — extending a term whose definition excludes it — and calls the same thing "the server": "The token, not the server, is the capability boundary" (l.33), "the MCP server needs no rewrite" (l.52).docs/agents/domain.md:43requires "the term as defined inGLOSSARY.md… Don't drift to synonyms," anddocs/index.md:14says the glossary is updated when "a term is coined, or one drifts." Neither the glossary nor a conflict flag was added.Unit/role count is wrong — verifiable defect, not taste.
"A third Ansible role and a second systemd unit land on a Guest sized for one Service" (l.69). The role already writes two units —
forgejo.service(ansible/roles/forgejo/tasks/main.yml:80) andforgejo-runner.service(:226) — so forgejo-mcp's unit would be the third; onlyroles/forgejoexists (ansible/roles/,site.yml), so its role would be the second. The record misstates what the change adds.Judgement calls (baseline smells)
passthroughrefusal (l.28-30), fallback off (l.35-39), and Consequences "no reusable Forgejo credential" (l.79-80). Decision+consequence echo is ADR-normal; the third pass adds no new cost.Privacy rule respected — only
10.12.0.141, already published indocs/architecture.md; no DDNS name or external IP. Index row and file added per convention; relative links resolve.Standards axis: 6 findings (2 hard, 4 judgement calls). Worst: the l.69 unit/role count.
Spec review — PR #43
Axis: does the diff match what the originating issue/spec asked for?
Spec source: Forgejo issue #21 — "ADR: the forgejo-mcp Service's shape and credential-less endpoint" (parent #17).
Diff:
git diff main...origin/hermes/21-forgejo-mcp-adr(commits632dff0,eefe98c).1. Wrongly implemented — role/unit/Guest counts contradict spec and repo
ADR line 69 (Consequences): "A third Ansible role and a second systemd unit land on a Guest sized for one Service". Every count is wrong. The repo holds one role (
ansible/roles/contains onlyforgejo;site.ymllists onlyforgejo) and two systemd units (forgejo.service,forgejo-runner.service). So the incoming role is the second role and its unit the third unit — the ADR swapped the two ordinals. The Guest is sized for two Services, not one: spec #17/GLOSSARY.mdline 71 — "Forgejo and the Actions runner are the Services on theforgejoGuest" — and the ADR itself says the pin lands "beside the other two Services' pins" (line 20). Directly contradicts #21's "a dedicated Ansible role for forgejo-mcp as a third Service on theforgejoGuest".2. Missing/partial — endpoint identity omits the port
#21 asks for "the shape of the forgejo-mcp Service and the identity of its endpoint". The ADR fixes transport (
/mcp), bind and allowlist host (10.12.0.141), but never states the port — "beside its port" (line 63) is the only mention, and grep for8089in the ADR returns nothing. #17 names it:http://10.12.0.141:8089/mcp. The endpoint's actual address is therefore not recorded.Scope creep
None. The intro framing, Consequences and Update-when sections are standard ADR format; the
--allowed-originsnote is required by #17's Implementation Decisions.Acceptance criteria met
passthroughover a configured token; fallback off; bind+allowlist fail-closed; loopback upstream; no Edge entry. ✅eefe98cis accurate: the host allowlist is aHost-header check, not a source-address control. ✅Spec axis: 2 findings (1 wrongly implemented, 1 missing/partial, 0 scope creep). Worst: the l.69 count error, which contradicts #21's "third Service".
Review addressed — commit
be769dcBoth review axes are answered in a new commit on the branch (
be769dc, plus a merge ofmainat72a7e2b). Diff againstmainis now three files:docs/adr/0003-...md,docs/adr/index.md,GLOSSARY.md.Standards review (comment 500)
docs/index.md:14):GLOSSARY.md's Service entry now reads "Forgejo, the Actions runner and forgejo-mcp are the Services on theforgejoGuest". Nothing else in the repo enumerates the Services, so the contradiction is closed in one place. The ambiguous "the server"/"the MCP server" referents (l.33, l.52) now name forgejo-mcp.ansible/roles/holds onlyforgejo(so incoming = second role); the role writesforgejo.service(tasks/main.yml:80) andforgejo-runner.service(:226) (so incoming = third unit); the Guest runs two Services.Judgement calls:
Spec review (comment 501)
8089and the full endpointhttp://10.12.0.141:8089/mcp, matching #17.The "third Service" phrasing (heading intro, l.9) is kept where it is spec-accurate — #21/#17 both call forgejo-mcp "a third Service" — and the glossary now agrees with it.
Merge conflict
mainadvanced to860ace7(PR #46, ADR 0004) while this was open; #46 deliberately left 0003 free, so no renumber was needed. The only conflict was both PRs adding a row todocs/adr/index.md; both rows are kept in numeric order (0003 then 0004).mergeable: true.make fmtexits 0; all relative links in the changed docs resolve in-repo.