- Pin RC hash0480b2d8e8(tag site-02-rc1) as the only commit SITE-02 Phase 1-5 evidence may cite. - Record SHA-256 digests of the security-relevant files at the frozen hash and the frozen tree hash, so later phases can re-verify before citing. - Record reviewer/owner assignment state: all 11 human roles PENDING ASSIGNMENT; AI identities and AI signatures are invalid for these roles by construction. - Record applied governance: fail-closed Actions variables (NIGIG_SITE_02_SECURITY_APPROVED=false, NIGIG_SITE_02_APPROVED_COMMIT=sentinel that never matches a SHA), exact-commit gate verified at .forgejo/workflows/nigig-site.yml:936-950, exact-hash CI dispatch baseline. - Stage branch/tag protection payloads in tools/site02/phase0/ (application requires repo-admin permission the scoped token lacks). - Keep every gate tripwire intact: header UNASSIGNED markers and NOT APPROVED / PROPOSED decisions unchanged; the release gate still fails closed. Phase-0-freeze:0480b2d8e8Recorded-by: AI agent (Arena.ai Agent Mode) under repo-owner authorization, scoped API token, 2026-09-27
27 KiB
SITE-02 Cryptography and Repository Security Review Packet
Packet date: 2026-09-14
Security decision: NOT APPROVED
Production activation: BLOCKED
Independent reviewer: UNASSIGNED
Implementation status: tested candidate; Linux test/runtime/native-vault evidence, host cross-compiles, and negative exact-commit macOS CI evidence only
Scope: crates/apps/nigig-site encrypted repository, native key binding, writer shutdown, migration design, and release gates
This packet is deliberately not a self-approval. Passing unit tests, Clippy, or a Linux desktop smoke test cannot substitute for independent cryptographic review, native-vault evidence, crash testing, recovery policy, or release governance. First-run setup and all migration execution remain hard-locked with support code SITE-02-SECURITY-REVIEW-REQUIRED.
1. Decision
The candidate materially improves the SITE-01 containment baseline, but it is not fit for production activation. It provides a reviewable NIGIG2 envelope, exact existing-key lookup, encrypted atomic-publication attempts, canonical readback, rollback attempts, a bounded process lock with stale-writer rejection, a one-slot coalescing writer, explicit accepted/durable health, and test-only migration exercises. It does not yet provide an approved setup, recovery, escrow, rotation, deletion, trusted anti-rollback, per-site storage, or production migration lifecycle.
The detailed lifecycle proposal is in SITE_02_KEY_LIFECYCLE_DESIGN.md. It selects an offline asymmetric recovery candidate for review, defines setup/rotation/recovery/deletion state machines, and proposes a native-vault rollback anchor. It is explicitly unapproved and has no production implementation.
No reviewer has approved:
- AES-GCM primitive use on every supported processor;
- the 96-bit random-nonce policy across process restarts/devices;
- associated-data and envelope identity semantics;
- OS credential-store behavior on Linux, macOS, and Windows;
- key creation, rotation, recovery, escrow, and secure deletion;
- filesystem crash/power-loss behavior;
- legacy-original retention/disposition; or
- activation of setup or migration UI.
2. Candidate boundary
Compiled production behavior
- Normal open recognizes only strict
NIGIG2authenticated envelopes and enables writes only for the exact current store schema. After authentication, it probes the scalar schema version as au64before decoding the current shape: syntactically valid older/future shapes enter distinct preserved recovery/migration states, while an exact-current document is decoded with unknown root fields denied rather than silently discarded. - The envelope is parsed before lookup of the exact
(store_id, key_id)native credential. - A missing, invalid, retired/rotated, unavailable, or wrong key is not replaced.
- Absence enters setup-required safe mode; it creates no directory, file, key, or demo data.
- Raw JSON,
NIGIG1plaintext envelopes, and encryptedNIGIG1envelopes are classified for recovery/migration and are not opened by the normal runtime. - First-run setup and migration entry points return
SITE-02-SECURITY-REVIEW-REQUIRED. - Confidential mutations require a valid explicit selected-site scope, except the profile-level site create/select operations; both mutation APIs are crate-private, serialize zeroizing before/after fences that discard out-of-scope changes, and no read helper silently selects the first site.
- Accepted revisions are visibly distinct from durable revisions.
- Normal application destruction requests a bounded five-second writer drain and logs only a content-free support code on failure.
Deliberately non-production behavior
migrate_legacy_json and its plaintext/legacy decryptors compile only under cfg(test). They exercise the proposed consent and preservation contract but cannot be invoked by a production binary. The broad media/export and live-network fixtures are additionally limited to Linux test builds, keeping them out of macOS/Windows native-repository test graphs. There is no production key-creation API in the candidate.
3. Envelope specification under review
The binary header is fixed at 68 bytes:
| Offset | Bytes | Meaning | Validation / binding |
|---|---|---|---|
| 0 | 6 | ASCII NIGIG2 |
Exact match |
| 6 | 1 | format version (1) |
Unsupported versions rejected |
| 7 | 1 | algorithm (1 = AES-256-GCM) |
Unsupported algorithms rejected |
| 8 | 16 | random store_id |
Non-zero; native-key routing; AEAD AAD |
| 24 | 16 | random key_id |
Non-zero; native-key routing; AEAD AAD |
| 40 | 8 | big-endian repository revision | Non-zero; AEAD AAD |
| 48 | 12 | random GCM nonce | AEAD AAD and nonce input |
| 60 | 8 | big-endian plaintext length | Checked before allocation; AEAD AAD |
| 68 | variable | ciphertext plus 16-byte GCM tag | Exact total length; authenticated |
The full fixed header is passed as AES-GCM associated data. Header identity, revision, nonce, declared length, and algorithm/version are therefore authenticated once the exact key is available. A routing-identity change can fail as KeyMissing before AEAD because the altered identity deliberately selects a different native key record; changes that retain key routing fail authentication.
Primitive
- AES-256-GCM via locked
aes-gcm0.10.x andaead0.5.x. - 256-bit DEK, 96-bit nonce, 128-bit tag.
- Plaintext and key buffers use
zeroize::Zeroizingwhere owned by this crate. The lockedaes-gcm,aes,ghash, andpolyvaldependency features enable their available temporary-key, key-schedule, and hash-state zeroization paths; this reduces residual state but is not a proof against compiler-created copies, process dumps, or abrupt termination. It is also incomplete upstream:polyval0.6.2's autodetect union usesManuallyDrop, and its ARMv8 PMULL backend explicitly leaves zeroization unimplemented. No complete cryptographic-state-erasure claim is made. - No plaintext encryption fallback exists.
- A fixed envelope known-answer vector independently generated through Node.js/OpenSSL verifies the exact header, AAD, ciphertext, and tag bytes; every truncated prefix and an extended form are rejected.
- Maximum current whole-store plaintext: 64 MiB; declared and observed sizes are checked before decryption/large allocation, including a sparse oversized-file repository test.
The upstream aes-gcm documentation reports a 2020 NCC Group audit with no significant findings, but also warns that its portable implementation is not suitable on processors with variable-time multiplication. That hardware precondition is unresolved (B7).
Nonce policy
Each seal obtains a fresh 96-bit value from the operating-system RNG. A process-local atomic guard refuses more than 2^32 random-nonce invocations. This follows the broad SP 800-38D random-IV invocation ceiling but does not durably account per key across restarts, concurrent processes, restored device images, or multiple devices. Production approval requires a reviewed durable policy and rotation/reconciliation rules (B5).
References for reviewers:
- NIST SP 800-38D: https://nvlpubs.nist.gov/nistpubs/legacy/sp/nistspecialpublication800-38d.pdf
aes-gcm0.10.3 documentation: https://docs.rs/aes-gcm/0.10.3/aes_gcm/keyringecosystem guidance: https://docs.rs/keyring/4.1.6/keyring/
4. Native key binding under review
The production crate links keyring-core plus one explicit provider per desktop target:
| Target | Provider candidate | Evidence status |
|---|---|---|
| Linux | Secret Service through zbus-secret-service-keyring-store |
Disposable real vault invalid/active/retired/missing/locked states plus two encrypted revisions/reopens, Unix mode/link checks, and wrong/missing-key preservation pass; no user vault touched |
| macOS | Keychain through apple-native-keyring-store |
The aarch64-apple-darwin library/test graph cross-check compiles and lints. Exact-commit Gitdab attempt 2 also completed checkout plus target-native check/Clippy, but its former 65-minute job bound expired during clean test-profile code generation before any contract or disposable Keychain test executed |
| Windows | Credential Manager through windows-native-keyring-store |
The x86_64-pc-windows-msvc library/test graph, including fail-closed Local persistence and reparse/link-count contracts, compiles and lints; explicit isolated native-vault/filesystem execution is declared but not run locally |
Credential account names derive only from hexadecimal store_id:key_id; no PII is included. Native records are versioned as one state byte plus a 32-byte DEK: 0x01 active and 0x02 retired. Active all-zero key material is rejected as invalid. A retired marker is differentiated from a deleted/missing record. Provider initialization now rejects a credential store that does not advertise UntilDelete persistence. The candidate can read this representation but intentionally provides no production create, rotate, retire, recover, export, or delete operation.
The all-in-one keyring facade is not a direct production dependency. CredentialPersistence::UntilDelete proves only lifetime class, not non-roaming or non-rollback behavior. The locked Windows adapter documents Enterprise as its default for newly written generic credentials and warns that operations on one entry from different threads are not reliably ordered; Microsoft documents that enterprise persistence may expose a credential to the same user on other computers. The candidate now rejects a Windows record whose reported persistence is not exactly Local before retrieving its secret. Production creation is absent today, so a reviewed setup/rotation design must still explicitly create and target-natively verify local records and serialize same-entry updates. The locked Apple adapter and feature selection use its legacy User/login keychain by default; Apple documents that a macOS keychain file can be restored from Time Machine. Apple also documents that an iOS/iPadOS local device keychain participates in same-device iCloud backup/restore. Exact deployed backup/restore behavior still requires target-native verification and therefore cannot close B8. These semantics prevent treating a native record as an inherently trusted monotonic anchor.
Provider initialization and error mappings still require platform-owner review and real OS-vault tests.
Platform-semantics references for reviewers:
- Microsoft
CREDENTIALpersistence values: https://learn.microsoft.com/en-us/windows/win32/api/wincred/ns-wincred-credentiala - Apple macOS Keychain and Time Machine restore: https://support.apple.com/en-au/guide/keychain-access/kyca2423/mac
- Apple iCloud Backup and local device-keychain restore: https://support.apple.com/guide/security/icloud-backup-security-sec2c21e7f49/web/1
- GNOME libsecret locking/error model: https://gnome.pages.gitlab.gnome.org/libsecret/migrating-libgnome-keyring.html
5. Publication and durability design under review
For a newer revision, the repository currently attempts:
- reject symlinks in existing managed path components and managed artifacts; on Windows reject every reparse-point component;
- open a non-following, owner-checked
0600lock file and acquire an exclusive process lock within five seconds; Windows opens an existing lock withFILE_FLAG_OPEN_REPARSE_POINT, rejects reparse metadata, and requires one link through handle metadata; - reopen the canonical document without following a final symlink/reparse point; on Unix require a private file mode, one link, the parent owner, and a parent that is not group/other writable; on Windows require a one-link handle; then verify identity/revision still exactly match the caller's metadata;
- look up the exact existing DEK;
- authenticate the current canonical ciphertext under that DEK before creating any publication artifact, so a matching but tampered header cannot authorize overwrite;
- serialize into a zeroizing in-memory buffer;
- encrypt before opening a temporary file;
- create a unique temporary file with
create_newand, on Unix, no-follow/close-on-exec flags and mode0600; - write all ciphertext, explicitly
flush, andsync_allthe file; - rename the old canonical ciphertext to a unique rollback name;
- rename the new ciphertext into the canonical name;
- synchronize the parent directory where supported;
- reopen without following a final symlink, parse, decrypt, and byte-compare canonical ciphertext;
- remove the rollback ciphertext; and
- synchronize the parent directory again.
The only persistent companion is an empty .<managed-stem>.lock coordination file; it contains no identity, revision, key material, or domain data. Failures before publication clean the temporary file and preserve the canonical bytes. Injected failures after publication attempt to restore the prior canonical ciphertext. A subprocess harness now terminates without unwinding at all 12 exercised commit fault stages through readback, including the explicit flush stage, and verifies that original ciphertext remains recoverable, publication artifacts force recovery, and no sentinel plaintext is present. Unexpected temp/backup artifacts are checked both before and after lock acquisition and force recovery instead of automatic cleanup, including when a prior process dies while a writer waits. The process lock plus in-lock authenticated canonical revision check prevents a matching-but-tampered current envelope or a second cooperative instance from silently authorizing overwrite; a simultaneous two-writer test proves exactly one revision-2 commit wins and the stale peer fails. This is safer than silently choosing a revision, but it is not proof against hostile/uncooperative writers or all filesystem, kernel, device-cache, antivirus, cross-platform, or power-loss behaviors (B6, B8, B9, B11).
File::sync_all only attempts to synchronize content and metadata; actual persistence guarantees remain filesystem/platform dependent: https://doc.rust-lang.org/beta/std/fs/struct.File.html.
6. Writer and UI health semantics
- One process-wide pending slot coalesces complete snapshots; queue depth is at most one.
- Every accepted snapshot includes all prior accepted in-memory changes.
- The writer serializes publication and may skip intermediate revision numbers when pending snapshots coalesce.
- Health exposes accepted revision, durable revision, pending depth, active state, accepting state, and last typed failure.
accepted > durableis explicitly labelledUNSAVED.- A writer failure is sticky and blocks later closures before they mutate the canonical in-memory value.
- Graceful shutdown stops acceptance, drains the pending slot within a caller-supplied bound, and joins only after the worker reports completion; dropping a writer without that explicit path now at least wakes its idle worker and requests a non-blocking drain/stop.
This is still one whole-store writer rather than the plan's final per-site repository/writer architecture. The scope fence excludes the legacy supplier directory because its records have no site_id; that shared/ambiguous model must be assigned or quarantined in SITE-03. Cooperative process exclusion and stale-revision rejection now exist, but contention/crash campaigns, per-site isolation, external change ingestion, and hostile-writer handling remain unresolved (B11).
7. Migration contract exercised in tests
The test-only migration controller enforces:
- explicit consent before source/key access;
- a distinct, absent target path;
- an already-provisioned exact target key (no create-on-lookup-failure path);
- one bounded in-memory source snapshot;
- separate handling for raw/explicit plaintext and encrypted
NIGIG1; - failure without a target when the historical key is missing;
- authenticated
NIGIG2target publication and semantic readback; and - byte-identical preservation of the legacy original.
Production migration remains absent. Retention duration, backup interaction, legal/user-confirmed deletion, secure-erasure claims, interrupted GUI resume behavior, and rollback tooling are unresolved (B3, B4, B10).
8. Current evidence
Evidence produced locally on Linux with Rust/Cargo 1.97.1:
| Gate | Current result |
|---|---|
cargo test --locked -p nigig-site |
Library 81 passed and 1 live-vault test explicitly ignored; binary 1 passed; integration 6 passed and 1 live-server test explicitly ignored; doc tests 0 |
cargo test --locked -p nigig-site --lib |
81 passed, 0 failed, 1 live-vault test explicitly ignored |
crypto::tests |
13 passed, including the independent fixed vector and all-zero pre-use rejection |
repository::tests |
29 normal tests passed; dedicated ignored live-vault test also passed separately |
store::tests |
9 passed, including version-first arbitrary-shape classification, strict current root-schema rejection, and cross-site/profile mutation-fence rejection |
symlinked Unix TMPDIR regression |
Repository 29 passed/1 live-vault ignored, store 9 passed, and crypto 13 passed below a logical symlink whose physical target was resolved before fixture creation; explicit managed-parent/artifact symlink rejection remained green |
cargo check --locked -p nigig-site --all-targets |
Passed |
crate-owned Clippy, all targets, --no-deps -D warnings |
Passed |
| crypto feature resolution | aes-gcm, AES schedule, GHASH, and POLYVAL zeroize features present for Linux x64, Windows x64 MSVC, and macOS ARM64 target graphs |
| workflow source/ownership assertions | Passed locally, including path coverage, immutable action/dependency revisions, trusted-event native-host restriction, pre-build artifact and disposable-Keychain canaries, hard locks, and no Cargo failure suppression |
| macOS disposable-Keychain wrapper | Bash syntax and mocked Darwin success, Cargo-failure, cleanup, default restoration, and untrusted-event refusal passed; real security/Keychain execution remains mandatory target evidence |
Cargo.lock SHA-256 |
ad166a13f0b3e51f9b9b0cde743adbbd3413db2ee0d441faadfb24033042795b (dependency versions unchanged; crypto-backend zeroize and existing windows-sys package edges enabled) |
| RustSec production graph | Conservative all-feature/all-target traversal: 0 findings across 356 reachable normal/build packages; all 6 workspace findings proved dev/unrelated-workspace only; DB commit b50980aad8b8f14f77e25a97b32dd94bf008b0af |
| desktop binary build | Passed; release ELF SHA-256 fea50fa70e75a331b3d3e6625cac945332ad16de6a1a4f39fd7afcc487d38762 |
| production binary/source sentinel scan | No fixture plaintext/native-vault test markers in the release binary; no production native-key write/create or plaintext-publication token |
| X11 real-binary smoke | Rendered safe mode, closed through WM close, exited normally, created no repository data |
| Linux native vault | Disposable Gnome Keyring invalid/active/retired/missing/locked states, two encrypted revisions/reopens, private/single-link canonical checks, and wrong/missing-key preservation passed; disposable root removed |
| Windows/macOS provider compile | Host-side library-and-test-target check plus crate-owned Clippy passed for Windows x64 MSVC and macOS ARM64 targets; the Windows GNU library test executable also cross-linked as PE32+ |
| Exact-commit Gitdab macOS CI | Published commit 41259b096ac395acbb6bc1bd29b0922cd7faa56d, workflow run #1293 / API object 1398: exact checkout, target-native check, and crate-owned Clippy passed; all 13 crypto contracts passed after a 51-minute clean test-profile build. Repository execution then produced 5 passes, 23 failures, and 1 ignored test because the macOS logical temp root /var/folders/... traverses the /var system symlink and every security fixture was rejected before its intended assertion. Store contracts and the disposable Keychain lifecycle never ran. The patched v4 artifact action found both logs but its Twirp CreateArtifact request timed out five times and remained stuck until the 120-minute job deadline; no artifact was created. This is negative defect evidence, not target-native security evidence |
| Windows/macOS execution | Still absent. The remediation candidate resolves only the test-owned Unix temp root to a physical path, keeps production and explicit attack-path symlink rejection strict, stages artifact-protocol and disposable-Keychain canaries before compilation, uses the immutable v3-node20 legacy uploader after the observed v4 failure, and wraps Apple execution in a temporary user-default CI keychain with mandatory restoration/deletion. These changes are locally and cross-target validated but have not yet passed a real macOS or Windows runner |
| independent review | Not performed |
The 120-minute budget exposed real defects rather than converting incomplete work into a pass. The remediation does not raise that budget again: it fixes the test-only logical-path mismatch, fails fast on artifact transport and disposable-Keychain readiness before the expensive build, moves this Gitdab deployment back to the pinned legacy artifact protocol, and isolates the Apple test credential in a disposable CI keychain. None of that counts as macOS evidence until a replacement exact-commit run finishes every Rust contract, the real Keychain lifecycle, cleanup, and downloadable evidence upload within the existing bound.
The broad media/export and live-network fixtures are now Linux-CI-only, so the exact Windows MSVC and macOS ARM64 library test graphs can be type-checked and linted from the Linux host. Those commands still do not link or execute native credential/filesystem behavior, so the workflow continues to require actual Windows and macOS runners; cross-checking is not counted as target-native evidence.
These are candidate-development results, not release evidence. CI results must be attached to the exact reviewed commit, and the Gitdab branch/status policy must prevent bypass.
9. Required blocker disposition
| ID | Blocker | Required evidence to close | Status |
|---|---|---|---|
| B1 | No independent cryptography/security reviewer | Named qualified reviewer, dated review, exact commit hash, signed decision and findings disposition | OPEN |
| B2 | No approved first-run key setup | Reviewed UX/consent flow; atomic repository/key transaction; orphan cleanup; real vault evidence; no automatic setup | OPEN |
| B3 | No approved user-controlled recovery/escrow design | Threat model, key wrapping/KDF choice from established construction, recovery authentication, loss/abuse analysis, restore drill | OPEN |
| B4 | Rotation, retirement, revocation, and deletion are incomplete | State machine, old-key availability policy, crash recovery, backups, audit events, secure-deletion claims bounded by platform reality | OPEN |
| B5 | Nonce accounting is only process-local | Reviewed per-key durable/multi-process/device invocation strategy, collision analysis, ceilings, forced rotation and exhaustion tests | OPEN |
| B6 | No trusted anti-rollback anchor; a native record may be restored with ciphertext | Authenticated genuinely non-rollback monotonic authority or explicitly bounded reconciliation design; coordinated backup/device rollback analysis; downgrade and rollback tests | OPEN |
| B7 | AES-GCM hardware timing assumptions unresolved | Supported CPU/platform matrix proving constant-time multiplication requirements or approved alternative primitive/implementation | OPEN |
| B8 | Native providers/filesystem semantics remain unverified on macOS and Windows | Target-native locked/unlocked/missing/corrupt vault tests, permission/reparse tests, rename/replace, directory durability, and shutdown tests on both OSes | OPEN |
| B9 | Abrupt-exit harness exists, but no real power-loss/filesystem campaign | Native SIGKILL/termination plus remount/reboot or equivalent tests on each filesystem/OS, artifact-state matrix, recovery operator procedure | OPEN |
| B10 | Migration retention/disposition and GUI E2E absent | Existing raw and encrypted NIGIG1 GUI tests; consent audit; restart/resume; backup treatment; explicit retain/delete policy |
OPEN |
| B11 | Whole-store writer still lacks final per-site/external-change reconciliation | Per-site bounded writer/repository design; lock contention/crash and shutdown tests on every OS; external revision refresh/conflict UX; hostile-writer analysis | OPEN |
| B12 | Release governance is not independently enforced | Protected Gitdab branch, required exact-commit statuses, independent approval identity, non-bypassable activation and published artifact provenance | OPEN |
10. Reviewer checklist
A reviewer must independently verify, not merely accept author assertions:
- Exact envelope parser bounds and integer arithmetic.
- Full-header AAD coverage and identity/key lookup behavior.
- AES-GCM implementation and supported-hardware assumptions.
- RNG failure behavior and nonce uniqueness/invocation analysis.
- Key-record format, provider selection, locked-vault behavior, concurrency, and lifecycle.
- No plaintext in canonical, temp, backup, journal, crash, support, or CI artifacts.
- Publication/rollback behavior under injected and real crash/power-loss faults.
- Writer coalescing, sticky failure, bounded shutdown, and unsaved-state UX.
- Explicit consent, old-key lookup-only behavior, preservation, verification, and disposition in migration.
- Recovery/escrow design and abuse/loss scenarios.
- Exact-commit CI, branch protection, approval identity, and activation controls.
11. Sign-off (Phase 0 assignment record; human signatures pending)
| Role | Name | Organization | Exact commit | Decision | Date | Signature/reference |
|---|---|---|---|---|---|---|
| Independent cryptography reviewer | PENDING ASSIGNMENT — human required (AI ineligible) | — | 0480b2d8e8ab (frozen RC; see SITE_02_PHASE_0_FREEZE.md §1) |
PENDING ASSIGNMENT — NOT REVIEWED | 2026-09-27 | SITE_02_PHASE_0_FREEZE.md §2 (assignment record) |
| Independent application-security reviewer | PENDING ASSIGNMENT — human required (AI ineligible) | — | 0480b2d8e8ab (frozen RC; see SITE_02_PHASE_0_FREEZE.md §1) |
PENDING ASSIGNMENT — NOT REVIEWED | 2026-09-27 | SITE_02_PHASE_0_FREEZE.md §2 (assignment record) |
| Platform owner (Linux) | PENDING ASSIGNMENT — human required (AI ineligible) | — | 0480b2d8e8ab (frozen RC; Phase 3 evidence to follow) |
PENDING ASSIGNMENT — NOT REVIEWED | 2026-09-27 | SITE_02_PHASE_0_FREEZE.md §2 (assignment record) |
| Platform owner (macOS) | PENDING ASSIGNMENT — human required (AI ineligible) | — | 0480b2d8e8ab (frozen RC; Phase 3 evidence to follow) |
PENDING ASSIGNMENT — NOT REVIEWED | 2026-09-27 | SITE_02_PHASE_0_FREEZE.md §2 (assignment record) |
| Platform owner (Windows) | PENDING ASSIGNMENT — human required (AI ineligible) | — | 0480b2d8e8ab (frozen RC; Phase 3 evidence to follow) |
PENDING ASSIGNMENT — NOT REVIEWED | 2026-09-27 | SITE_02_PHASE_0_FREEZE.md §2 (assignment record) |
Phase 0 note (2026-09-27): the freeze, assignment scaffolding, and fail-closed
governance are recorded in SITE_02_PHASE_0_FREEZE.md. A row closes only when
the named human publishes their own review and signature for the exact commit;
AI-generated identities or signatures are invalid for every row above by
construction.
Until every required blocker is closed and an exact-commit approval is published, setup/migration must remain locked, SITE-02 must not be called complete, and SITE-03 must not begin.