This repository has no description
Rust 100%
Go <1%
<1%
Just <1%
Nix <1%
Dockerfile <1%
HTML <1%
Shell <1%

README.md

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. import detects them and refuses unless --allow-lfs-pointers is given; git lfs pull against 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.rs is the table); other (limit, cursor) tuples return InvalidRequest.
  • 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 return InvalidRequest.
  • no firehose. com.atproto.sync.subscribeRepos and /events are 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