knot-static #
a read-only Tangled knot whose public serving path is only nginx + immutable files.
knot-static is an offline admin/compiler CLI: provisioning mints real repo DIDs and writes
knot2-compatible meta.git state; generate compiles public state into immutable generations
(precomputed XRPC responses, generated nginx maps, sanitized dumb-HTTP git exports); activate
swaps generations atomically behind an nginx -t gate.
design packet and history live in the tangled monorepo under knot2/crates/knot-static
(this repo is that crate, extracted).
vendored core #
knot2 crates are consumed as path deps into a vendored slice of the tangled monorepo
at vendor/core (sv-fe branch, see vendor/core/REV). sv-fe carries knot2 APIs
(knot_git::RefClass, knot_types::ContributionPolicy, PreparedRepoDid::operation,
Registration.policy) that master doesn't have yet.
the slice contains the workspace root manifest, the three root crates the closure needs
(knot-capability, lexicons, trusted-proxies), and all of knot2/ — including the
vendored gix-pack fork that knot-pack needs via [patch.crates-io]. no git dependencies,
so no hash bookkeeping.
to move to a newer sv-fe:
scripts/sync-core.sh /path/to/tangled-checkout # checkout must be on sv-fe
cargo test && nix build .#knot-static
git commit -a
build #
nix build .#knot-static # release binary
cargo test # unit + parity + plc journal tests
cargo-hashes.nix holds the nix outputHashes for the pinned git deps (one fetch, shared
by all knot2 crates). regenerate by setting a fake hash and copying the hash nix reports on mismatch.
v1 boundaries (deliberate, documented, not bugs) #
- no git writes. no receive-pack, no smart upload-pack POST, no write XRPC routes. provisioning is an explicit admin action on the build machine.
- no SSH git transport. HTTPS clone/fetch (dumb HTTP) only.
- no shallow clones.
git clone --depth ...fails: dumb HTTP doesn't implement shallow negotiation. clients get full dumb-HTTP clones; partial clone/filter is not promised. - no standard git LFS. LFS pointer files are served as plain content.
importdetects them and refuses unless--allow-lfs-pointersis given;git lfs pullagainst this knot will not work. LFS batch APIs are not implemented and not advertised. - finite revision profile. accepted revspecs: empty (HEAD), short/full branch, short/full
tag, full object-format-width commit OID. no rev expressions (
main~3,HEAD^2), no abbreviated OIDs. - bounded compare. default-branch tip <-> live branch tips, both directions, knot2's
500-commit bound. other pairs return
CompareError/InvalidRequest, not nginx 404s. - profiled pagination. only the page shapes the Tangled clients use
(
src/project/pagination.rsis the table); other(limit, cursor)tuples returnInvalidRequest. - blame is optional and disabled by default in v1 (
profile.blame): knot2 itself has no blame engine in this tree; eligibility/bounds are implemented, generation is enumerated at tips. - no archive
prefix=. default prefix only; custom prefixes returnInvalidRequest. - no firehose.
com.atproto.sync.subscribeReposand/eventsare not served. mirrors are notified via configured resync triggers after provisioning/activation ([mirror]config).
serving model #
generate writes a generation: manifest.json (routes -> CAS objects), www/_responses
(hardlinks into the shared CAS), www/_git (sanitized public-only exports), nginx/
(maps + config, generation-absolute paths). activate validates and atomically swaps
active.conf + current. old nginx workers keep their old generation paths. until reliable
worker-drain acknowledgement exists, GC retains all published generations regardless of
keep_generations. storage is therefore unbounded. CAS sweep marks every valid retained
manifest and aborts before deletion if any manifest cannot be validated.
generation builds use private staging, a completion inventory, and atomic publication under
a per-output-root admin lock. a repeated ID validates and reuses the published tree without
replacing its files. nginx PID, log, and temp files live under output/runtime/, outside
the immutable generation payload.
commands #
knot-static --config <path> [default: knot-static.toml, env: KNOT_STATIC_CONFIG]
init
repo import --owner <did> --rkey <rkey> [--name <name>] [--default-branch <branch>]
[--source <at://...>] [--allow-lfs-pointers] <path>
repo create --owner <did> --rkey <rkey> [--name <name>] [--default-branch <branch>]
repo list
repo record <repoDid> [--publish] [--pds-handle <handle>]
[--pds-password-env <env>] [default env: KNOT_STATIC_APP_PASSWORD]
generate [--repo <repoDid>]
activate <generation>
plc sync