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).
81 lines
4.1 KiB
Markdown
81 lines
4.1 KiB
Markdown
# 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 (bare `just` never 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` — removes `dist/` and `server` (keeps `tools/` and
|
|
restores the committed `dist/.gitkeep`).
|
|
- The shiroa toolchain is pinned and self-bootstrapped into `tools/` by the
|
|
`tools` recipe. 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.typ` lists 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 `.typ` replaced by `.html`.
|
|
- `cmd/parser` — build step: scans `docs/` for `#let access-groups = (..)`
|
|
tags and emits `acl.json`. Untagged pages are public.
|
|
- `cmd/server` + `embed.go` (repo root) — `//go:embed` of `dist/` and
|
|
`acl.json` into 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.json` aborts 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.in` files fetched by client-side JS.
|
|
`acl.PageKey` reduces 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.json` contains 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 Justfile
|
|
`shiroa-version` and `.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/` and `acl.json` are embedded at Go build time; there is no runtime
|
|
reload. After changing docs or ACL, rebuild the binary.
|
|
- `go build` needs `dist/` and `acl.json` to exist: fresh clones carry
|
|
`dist/.gitkeep` and a committed `acl.json` for 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 build` and `just run` must succeed
|
|
with no global shiroa on PATH. Test the bare `just` output too. After
|
|
any clean/test cycle, `git status` must be clean: `dist/.gitkeep` is
|
|
tracked, and committing a worktree where it is missing breaks fresh
|
|
clones (`go:embed` needs `dist/`).
|
|
- 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 in `internal/acl` and `internal/middleware`
|
|
whenever 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.
|