Independent review (second attempt) passed all 7 verification steps at
19f6007, including the fresh-clone embed check, full build from clean
state (9.7s), server curl matrix (public 200, protected + all theme
sidecars 503, searchindex 404, no content in 503 bodies), and just check.
Findings applied:
- README: prerequisite Go version corrected to 1.27.1+ to match go.mod
(a Go 1.22-1.27 user got a hard 'requires go >= 1.27.1' failure).
- CI: upload-artifact v3 -> v4 with a fallback note for older runners;
CI now also runs the gofmt guard like 'just check'.
- Justfile: curl -fsSL so a bad download fails at curl instead of tar;
comment clarifying that --dest-dir is resolved relative to docs/.
- AGENT.md: version-pairing trap now covers both pin locations
(Justfile and .gitea/workflows/build.yml).
4.1 KiB
4.1 KiB
AGENT.md — operating rules for agents working on Yggdrasil
Personal knowledge base: Typst sources (docs/, a shiroa book) compiled to
a static site, embedded into a Go binary that enforces per-page OIDC group
permissions (Authentik). README.md is the human-facing documentation; this
file is the working rulebook. Follow it; when it conflicts with the user,
the user wins.
Commands
just— shows the command overview (barejustnever builds anything).just build— parse ACL, compile the book, build./server.just run— build everything, then serve on :8080.just check— go vet, go test, gofmt. Run it before every commit.just clean— removesdist/andserver(keepstools/and restores the committeddist/.gitkeep).- The shiroa toolchain is pinned and self-bootstrapped into
tools/by thetoolsrecipe. Never assume a global shiroa/typst install exists; do not install shiroa globally; do not bump the pin casually (see Traps).
Layout
docs/— shiroa book.book.typlists every built page; files not listed there are not part of the site. Categories are=part headers in the summary, pages are# chapter("path.typ")[Title]entries. Final URL = source path with.typreplaced by.html.cmd/parser— build step: scansdocs/for#let access-groups = (..)tags and emitsacl.json. Untagged pages are public.cmd/server+embed.go(repo root) —//go:embedofdist/andacl.jsoninto a single binary.internal/acl— ACL loading and page-key normalization (security critical).internal/oidc— OIDC authorization-code client.internal/middleware— sessions, login/callback/logout, access gate.legacy_docs/— frozen previous site. Never modify or migrate it unless the user asks explicitly.
Security invariants — do not break
- Fail closed: a malformed
acl.jsonaborts startup; protected pages with no configured IdP answer 503, never the content. - Page keys: shiroa pages keep their rendered content in sibling
<page>.<theme>.multi.sir.infiles fetched by client-side JS.acl.PageKeyreduces any URL to a page key so those files inherit the page's protection. If the shiroa output format changes, re-verify this mapping: content in any new sidecar file must be covered or blocked. /searchindex.jsoncontains the text of protected pages: blocked at the router, always.- Session cookies: HttpOnly, SameSite=Lax, Secure unless COOKIE_SECURE=false. OAuth2 states are random and single use; post-login redirect targets are sanitized (open-redirect guard).
Traps
- Shiroa version pairing: the CLI and the
@preview/shiroa:*typst package must match (CLI v0.4.0-rc2 pairs with package 0.4.0; the v0.3.0 release CLI rejects 0.4.0 books). The pin lives in two places: the Justfileshiroa-versionand.gitea/workflows/build.yml. Bump all of them together with the package import and rebuild. - Typst call syntax: no space between
)and the trailing content block —#chapter("x.typ")[Title], not#chapter("x.typ") [Title]. dist/andacl.jsonare embedded at Go build time; there is no runtime reload. After changing docs or ACL, rebuild the binary.go buildneedsdist/andacl.jsonto exist: fresh clones carrydist/.gitkeepand a committedacl.jsonfor exactly this reason. Never gitignore those two.
Testing expectations
- Test what the user runs, not a proxy for it: from a clean state
(
rm -rf tools && just clean),just buildandjust runmust succeed with no global shiroa on PATH. Test the barejustoutput too. After any clean/test cycle,git statusmust be clean:dist/.gitkeepis tracked, and committing a worktree where it is missing breaks fresh clones (go:embedneedsdist/). - Server behavior: curl a public page (200), a protected page (503 without
OIDC env; it must never serve protected content), and
/searchindex.json(404). Extend the unit tests ininternal/aclandinternal/middlewarewhenever touching those paths. - A live OIDC round trip needs a real IdP and is not testable here. Say that explicitly when reporting what was verified.