test: eliminate shared fixture races and timing-dependent assertions
nostr:nevent1qgsx2lyl2e4zvfadwcvkd9fkrcwczj7mf858hy85mwqclwgut8wpg2spz3mhxue69uhhyetvv9ujumn8d96zuer9wcq3yamnwvaz7tm8d96xummnw3ezucm0d5q3kamnwvaz7tmwva5hgtnyv9hxxmmwwashjer9wchxxmmdqqsfdx93lyfyyuvw79qg8qe4eec4gjxkqasu3glrrsljhdgle3hw5fqtx4mfz
PR-Author: DanConwayDev's Agent
nostr:npub1v47f74n2ycn66asev62nv8sas99akj0g0wg0fkup37u3ckwuzs4q7cwtp0
CoverNote:
Parallel tests could lose released relay ports, leave connections alive after fixture shutdown, block Git children on full pipes, or infer readiness from scheduler-dependent sleeps. This revision extends the existing relay-port PR into one consolidated reliability proposal for Grasp.
- Retain SDK relay listeners through startup and exercise 32 concurrent fixtures.
- Transfer reserved loopback sockets into Grasp subprocesses through a private Unix test protocol, retain the address across restart, and keep offline/recovering endpoints continuously owned.
- Own HTTP, WebSocket upgrade and backend-forwarding tasks across the mock relay, auth gate, Git servers and proxy fixtures. Explicit shutdown cancels and joins requests; Git subprocess I/O drains concurrently.
- Wait for event visibility and actual connected-state metrics; correct metrics URL construction and stopped-source bookkeeping.
- Replace expiry scheduling margins with explicit timestamps and streaming sleeps/frame assumptions with observable gates and byte-level assertions.
Tests remain enabled and parallel. Normal server binding and service configuration are unchanged; inherited listener support requires NGIT_TEST=1 and validates a listening loopback socket. The private capability probe lets the companion ngit harness retain compatibility with older releases.
Targeted Linux validation in the repository development shell: 65 relay-connection tests, 33 rejected-index tests, 132 purgatory tests, all 3 streaming tests, 7 shared Git-server tests, and relay-identity/recovery tests passed. Final fixture_lifecycle passed 57 tests; the moved subprocess-restart regression passed separately. Scoped Clippy for fixture_lifecycle and relay_identity passed with warnings denied. A smoke test queried this Grasp binary using the modified ngit harness. Formatting and whitespace checks pass.
These checks are scoped, not a claim of full workspace, nixpkgs sandbox or Darwin validation. Full host builds remain outstanding for the new changes. External Buzz service fixtures are outside this PR. The separate ngit reliability proposal addresses the equivalent harness and Blossom timing issues.
AI assistance: implemented and reviewed with Codex (GPT-6).
Document the private test listener protocol, bounded observable readiness,
connection ownership and controlled timing assertions so new fixtures do
not reintroduce the same races. Replace advice to rerun transient failures
with instructions to preserve evidence and diagnose the underlying cause.
The listener environment is test-only rather than deployment configuration;
normal service configuration and the NixOS module are deliberately unchanged.
Validation: checked guidance against the implementation and targeted test
commands; formatting and whitespace checks pass.
Assisted-by: Codex (GPT-6)
Stopping accept loops left detached relay/proxy sessions alive. Track HTTP,
WebSocket upgrade and forwarding tasks under their owning fixture, cancel
them on shutdown, and drain cancellation before explicit stop returns.
Preserve censoring, rate limits, authentication and simulated disconnect
behavior. Add regressions that observe a live protocol exchange before
asserting the connection closes on stop, all under bounded deadlines.
Validation: fixture lifecycle checks passed for censoring, REQ/NEG limiting,
flapping and setup-drop relays. Auth-gating and upload proxy shutdown
regressions passed through relay_identity's common helper tests.
Assisted-by: Codex (GPT-6)
Detached HTTP tasks could outlive stopped Git fixtures. Sequential stdin and
output handling could deadlock when a child filled stdout or stderr before
consuming input. Own connection tasks, cancel Git children with their request,
and drain both outputs concurrently with stdin.
Keep fixture HTTP semantics and repository layout unchanged. A bound owned
listener replaces the unnecessary startup grace loop. Explicit stop joins
cancelled tasks while synchronous drop aborts the owner.
Validation: all seven shared SmartGitServer tests passed, including live
connection shutdown and one-MiB concurrent pipe traffic.
Assisted-by: Codex (GPT-6)
Fixed startup and promotion sleeps could expire before asynchronous work
completed. Wait with bounded deadlines for event visibility and an actual
connected gauge; connection attempts and health do not prove readiness.
Preserve URL schemes when constructing metrics endpoints, retain ownership
of unavailable endpoints, and remove stopped sources instead of replacing
them with a live placeholder. Keep immediate assertions where completion
already guarantees the result. Production sync policy is unchanged.
Validation: shared sync helper tests passed; relay_identity and the fixture
lifecycle target compiled and passed with the revised common helpers.
Full sync and deletion integration execution remains host validation.
Assisted-by: Codex (GPT-6)
Releasing a reservation before spawn or recovery allowed unrelated tests to
take the same port. Transfer the socket across exec through a private Unix
test-only protocol, retaining a parent copy across Grasp restarts. Validate
the inherited descriptor and keep it out of Git and SSH descendants.
Offline identity fixtures retain an accept-and-close endpoint until recovery.
Mock relay listeners and upgraded connections remain owned until shutdown.
Use real HTTP or SDK readiness instead of grace sleeps; startup errors in
the owned-listener path are not retried.
Normal server startup and deployment configuration are unchanged. The
capability probe permits older external harnesses to remain compatible.
Validation: listener adoption unit tests passed; relay_identity passed with
recovery coverage, MockRelay shutdown passed, and a real subprocess restart
plus a cross-repository ngit harness smoke test passed. Full platform builds
remain host validation.
Assisted-by: Codex (GPT-6)
Sleeping fake Git processes and assumed HTTP frame boundaries made streaming
checks depend on scheduler timing. Hold fake Git behind an owned loopback
gate until the test observes the expected prefix; collect bytes independently
of frame splits and explicitly release subprocess progress.
Keep terminal-flush, promotion and cleanup ordering assertions. Gate waits
have bounded deadlines; no production streaming code changes are included.
Validation: all three git_response_streaming tests passed.
Assisted-by: Codex (GPT-6)
Short real-time TTLs and sleep-based ordering made cache and purgatory tests
sensitive to host load. Age private cache timestamps explicitly and compare
queue or persistence deadlines against timestamps bracketing the operation.
Production clocks and expiry policy are unchanged. Retain tests for both
cache tiers, remaining TTL, queue deadlines and persisted expiry values.
Validation: 33 rejected-index and 132 purgatory tests passed in the offline
repository development shell.
Assisted-by: Codex (GPT-6)
The SDK local relay probes and releases an available port before run binds
it again. Another socket can claim that port in between, causing AddrInUse
in parallel relay-connection tests. Passing port zero alone also leaves the
SDK URL pointing to zero in the locked dependency version.
Use a test-only listener bound to loopback port zero and retain it for the
fixture lifetime. Upgrade HTTP connections and pass their streams to the
SDK relay handler, preserving relay policies and subscription behavior.
Own connection tasks in a JoinSet and abort them with the fixture. Move all
ten affected test relay startups to this fixture; production is unchanged.
Add coverage that starts 32 relays concurrently and connects to their
unique actual bound addresses, using bounded handshake/close deadlines.
Do not disable tests, serialize the suite, or retry occupied ports.
Validation: all 65 relay-connection tests pass, including the previously
failing tail-group restoration test and the concurrent-listener regression.
Full Rust formatting and whitespace checks pass. Backport applies cleanly
to 3.0.2. Full nixpkgs host validation remains pending.
Assisted-by: Codex (GPT-6)
test(sync): simulate downtime in checkpoint integration test
nostr:nevent1qgsx2lyl2e4zvfadwcvkd9fkrcwczj7mf858hy85mwqclwgut8wpg2spz3mhxue69uhhyetvv9ujumn8d96zuer9wcq3yamnwvaz7tm8d96xummnw3ezucm0d5q3kamnwvaz7tmwva5hgtnyv9hxxmmwwashjer9wchxxmmdqqswmj2l9wqv8gnmety86n59j6j2vnmdr4rvg3nhlsnadudp4v0nuggz2q9ml
PR-Author: DanConwayDev's Agent
nostr:npub1v47f74n2ycn66asev62nv8sas99akj0g0wg0fkup37u3ckwuzs4q7cwtp0
PR description:
The checkpoint integration test can fail on a loaded builder because its 75 ms sleep leaves only 25 ms before the cold index expires. The nixpkgs 3.0.2 build observed `cold_expired == 1` where the test expected zero.
Simulate two minutes of downtime by editing the temporary checkpoint's `saved_at` field, with hot/cold retention periods of one minute and one week. Keep every existing assertion and leave production behavior unchanged. This extends the approach already applied to the equivalent unit tests in commit 14170f202f to the integration test that retained the sleep.
Validation: independent review, Rust formatting, whitespace checks and application to the released 3.0.2 source passed. Full Rust test execution remains pending in CI and the host nixpkgs build. The proposed nixpkgs package carries the matching test-only patch until a release includes the correction.
AI assistance: Codex (GPT-6) prepared the correction and PR, with an independent agent review. Publication uses dcagent.
The persistence integration test sleeps 75 ms between 50 ms hot-cache and
100 ms cold-index expiry deadlines. Snapshot writes and host scheduling
can consume the remaining margin, legitimately expiring both tiers.
A sandboxed nixpkgs build exposed this after the streaming tests passed.
Set the persisted saved_at timestamp back two minutes and use minute/week
retention periods. This follows the synthetic-checkpoint approach already
used by the rejected-index unit tests in 14170f202f.
Retain all assertions so restoration must expire hot data while preserving
cold metadata. Production persistence and expiry behavior are unchanged.
Validation: independent review, rustfmt, git diff --check and patch
application to the released 3.0.2 source pass. Full test execution remains
pending in upstream CI and the host nixpkgs build; no heavy build ran here.
Assisted-by: Codex (GPT-6)
nostr:nevent1qgsx2lyl2e4zvfadwcvkd9fkrcwczj7mf858hy85mwqclwgut8wpg2spz3mhxue69uhhyetvv9ujumn8d96zuer9wcq3yamnwvaz7tm8d96xummnw3ezucm0d5q3kamnwvaz7tmwva5hgtnyv9hxxmmwwashjer9wchxxmmdqqs9gevtf3cnx2t8m5h7cxyzcda7ygcf9pld6kdkcq4l90sfcr2m45qvcet5s
PR-Author: DanConwayDev's Agent
nostr:npub1v47f74n2ycn66asev62nv8sas99akj0g0wg0fkup37u3ckwuzs4q7cwtp0
PR description:
Crates.io publication is a manual release step even though Nostr CI already validates tagged commits. Publish after the existing lint, formatting and test steps succeed, using a repository-scoped CARGO_REGISTRY_TOKEN secret only in the publication step.
Require a push event on a v-prefixed tag and an exact manifest-version match. Use Cargo package verification and --locked against the explicit crates-io registry. Only ngit-grasp is selected; grasp-audit remains unpublished.
This assumes the coordinator has the scoped token configured and the tagged commit contains this workflow. It does not change archive/NIP-82 publication, stable promotion, documentation sync or registry versions; no live crate was uploaded during development.
Validated workflow syntax with actionlint, matching/mismatched tag and missing-secret cases with an intercepted publish command, Cargo packaging without upload, cargo fmt --all -- --check, and git diff --cached --check.
Crates.io publication is a manual release step even though Nostr CI already validates tagged commits. Publish after the existing lint, formatting and test steps succeed, using a repository-scoped CARGO_REGISTRY_TOKEN secret only in the publication step.
Require a push event on a v-prefixed tag and an exact manifest-version match. Use Cargo package verification and --locked against the explicit crates-io registry. Only ngit-grasp is selected; grasp-audit remains unpublished.
This assumes the coordinator has the scoped token configured and the tagged commit contains this workflow. It does not change archive/NIP-82 publication, stable promotion, documentation sync or registry versions; no live crate was uploaded during development.
Validated workflow syntax with actionlint, matching/mismatched tag and missing-secret cases with an intercepted publish command, Cargo packaging without upload, cargo fmt --all -- --check, and git diff --cached --check.
Promote the already-reviewed release automation, documentation export metadata, canonical project references, and deterministic packaging fixtures as a stable maintenance release without changing production runtime behavior.
Move the accumulated Unreleased entries into a dated v3.0.2 section and align the Cargo and Nix package versions. The implementation remains isolated in the preceding merge commits.
Correctness relies on the Cargo manifest, lockfile, and Nix module declaring the same version. The OCI release behavior was validated by the green pull-request workflow, and the documentation exporter remains independently reviewable in its merge.
This commit deliberately excludes runtime changes, a grasp-audit version bump, the release tag, and publication. Validation is limited to release-metadata consistency and diff checks; the full suite is left to CI.
nostr:nevent1qqswjfxjs5ckkaqrnnz64ds0uj5csfpqukdncffgdw0cgup844e85mspz3mhxue69uhhyetvv9ujumn8d96zuer9wc0ewdc3
PR-Author: DanConwayDev's Agent
nostr:npub1v47f74n2ycn66asev62nv8sas99akj0g0wg0fkup37u3ckwuzs4q7cwtp0
PR description:
Documentation builds need a source-owned view of ngit-grasp commands and runtime configuration without copying Clap help or operator defaults into another repository.
Add an early hidden __docs-export dispatch that serializes schema-v1 command metadata, deduplicated public environment options, runtime constraints, dotenv behavior, and relay-owner secret precedence. Clap remains authoritative for reflectable facts; a narrow explicit registry covers validation and the skipped secret.
Correctness assumes Config::validate relationships continue to be mirrored in the constraint registry when they change. Secret source metadata intentionally contains locations and precedence only, never loaded values.
This does not publish artifacts, generate ngit.dev pages, expose internal test switches, model conditional Clap requirements that stable reflection cannot expose, or change normal relay and maintenance-command behavior.
Validated with cargo fmt --check, targeted unit and startup-isolation integration tests, all-target/all-feature clippy with warnings denied, deterministic process-output comparison, and jq schema assertions.
nostr:nevent1qqswgup4ksq366entxj3x839l25wvaul64jysk7jjdc90x0espxt4lqpz3mhxue69uhhyetvv9ujumn8d96zuer9wc2zhwln
PR-Author: DanConwayDev's Agent
nostr:npub1v47f74n2ycn66asev62nv8sas99akj0g0wg0fkup37u3ckwuzs4q7cwtp0
PR description:
Build release images as OCI layouts, publish their signed tag maps and blobs through Nostr and Blossom, and verify the public ncontainer pull before completing a release.
The deployment PR pipeline now imports and runs the exact OCI layout produced by the shared release helper. A maintainer-only backfill workflow can publish and verify an exact prior version such as 3.0.1 without moving latest. This PR deliberately leaves package versions at 3.0.1; the v3.0.2 bump follows only after CI and merge.
Motivation: Release images should be built, checked, published, and consumed through the same Nostr-native OCI path operators will use, without relying on an unreviewed local release procedure.
Approach: Add a checked-in container manifest, a Docker-to-OCI layout helper, automatic tag publication and pull verification, a safe exact-tag backfill workflow, and deployment CI that imports and runs the exact generated layout.
Correctness: Release tags come only from reviewed OCI index annotations; ordinary publication preserves prior tags; historical backfills cannot move latest or prerelease channels; generated images and temporary resources use bounded, validated names and cleanup.
Excluded scope: This change does not alter ngit-grasp runtime behavior, change package versions, create v3.0.2, publish a container, move a release tag, or run the heavyweight container build in the coding VM.
Validation: git diff --check; shellcheck on all container scripts; actionlint on all affected workflows; ngit parsing of .ngit/containers.yaml; canonical source and v3.0.1 tag resolution. The PR pipeline performs the full OCI build, import, and deployment test.
Assisted-by: Codex (GPT-5)
Two subprocess tests invoked git init through an inherited stdout and stderr without quiet mode. Clean Nix builds therefore printed Git's default-branch advice and repository initialization message twice in the test log.
Initialize the fixture explicitly on main and pass --quiet so it is independent of ambient branch configuration and emits no routine setup output. Production subprocess behavior is unchanged.
Validation: cargo fmt --all -- --check; cargo test -p ngit-grasp --lib git::subprocess::tests:: -- --nocapture.
Assisted-by: Codex (GPT-5)
The shallow-backup fixture populated refs/heads/main but left the bare repository HEAD on Git's configurable default branch. In clean Nix builds that default is master, so clone succeeded as an empty repository and never wrote the shallow marker the test requires.
Initialize the fixture explicitly on main so HEAD names the populated branch and the depth-one clone deterministically exercises shallow migration behavior. Production migration code is unchanged.
Validation: cargo fmt --check; cargo test -p ngit-grasp --lib shallow_legacy_backup_waits_for_family_closure.
Assisted-by: Codex (GPT-5)
The maintainer implementation guide moved out of the ngit client repository so implementations need a stable cross-project authority.
Link the canonical ngit-docs source and published guide from agent guidance and the relay architecture, and update the repository-state decision to name that guide as its model.
This assumes the source and published locations recorded by ngit's move commit are canonical. Runtime behavior and the existing relay-specific model description are deliberately unchanged.
Validated with git diff --cached --check and a repository-wide stale-reference scan.
Move current GRASP specification links from GitHub to GitWorkshop.
Link audit output to its exact pinned specification commit.
Use NIP-05 cloning for ngit-grasp and ngit.dev for the ngit homepage.
Leave the archived migration link unchanged as a historical record.
Validated with rustfmt and the 54-test grasp-audit library suite.
Derive the NIP-82 channel from the first prerelease identifier in the tagged version, while retaining main for stable versions. Pass the result explicitly to ngit so rc, beta, and similarly named channels are not mislabeled by the manifest default.
The application metadata and publication targets are unchanged. This deliberately does not trigger or otherwise rehearse a production release.
Validated with actionlint, yq, git diff --check, and stable/rc/beta/nightly derivation examples.
Motivation: NIP-82 asset events already bind release files to their SHA-256, while publishing a second platform-agnostic SHA256SUMS asset is redundant and rejected by catalogs that require an f tag.
Approach: package and upload only the static archive, document the signed-checksum model in the manifest, and derive the release event timestamp from Git creatordate so annotated and lightweight tags both produce stable dates.
Correctness: the archive remains deterministic and actions/upload-artifact retains the exact distributable; ngit signs its hash, size, MIME type, filename, URL, and Linux platform in the kind-3063 event.
Excluded: the already-published 3.0.1 release is not replaced, and truly multi-platform release layouts remain unchanged.
Validation: actionlint accepts the workflow; Git creatordate reproduces the v3.0.0 and v3.0.1 tag timestamps; ngit parses the manifest and reaches the expected publisher-author guard without signing or uploading.
nostr:nevent1qgsx2lyl2e4zvfadwcvkd9fkrcwczj7mf858hy85mwqclwgut8wpg2spz3mhxue69uhhyetvv9ujumn8d96zuer9wcq3yamnwvaz7tm8d96xummnw3ezucm0d5q3kamnwvaz7tmwva5hgtnyv9hxxmmwwashjer9wchxxmmdqqs22ta92yrt4cmtklv9k5luw6t576d3z4nd0tfqud3s6swdcfrcxjgrmf5tu
PR-Author: DanConwayDev's Agent
nostr:npub1v47f74n2ycn66asev62nv8sas99akj0g0wg0fkup37u3ckwuzs4q7cwtp0
PR description:
Motivation: make tagged ngit-grasp builds discoverable as complete NIP-82 releases without retaining the legacy zsp publication path.
Approach: commit the application and asset manifest, install ngit through setup-ngit v3, and publish with the established dcdev nbunksec after reproducible archive packaging.
Correctness: derive version and commit from the exact tag, enforce the expected application author and strict metadata, replicate to the pinned Primal, Ditto, and Haven Blossom servers, and publish to repository, catalog, and release relays.
Security: place the CI nbunksec in a mode-restricted temporary file and remove it on exit; no signing material is stored in the repository or process arguments.
Excluded: v3.0.1 predates this workflow and will be backfilled manually from its exact tag; automatic publication begins with the next genuine version tag.
Validation: actionlint accepted the workflow, and ngit parsed the manifest before stopping at the expected dcdev publisher guard without uploading or signing.
Motivation: make tagged ngit-grasp builds discoverable as complete NIP-82 releases without retaining the legacy zsp publication path.
Approach: commit the application and asset manifest, install ngit through setup-ngit v3, and publish with the established dcdev nbunksec after reproducible archive packaging.
Correctness: derive version and commit from the exact tag, enforce the expected application author and strict metadata, replicate to the pinned Primal, Ditto, and Haven Blossom servers, and publish to repository, catalog, and release relays.
Security: place the CI nbunksec in a mode-restricted temporary file and remove it on exit; no signing material is stored in the repository or process arguments.
Excluded: v3.0.1 predates this workflow and will be backfilled manually from its exact tag; automatic publication begins with the next genuine version tag.
Validation: actionlint accepted the workflow, and ngit parsed the manifest before stopping at the expected dcdev publisher guard without uploading or signing.
Motivation: Publish the narrowly scoped maintainer-authority correction and its follow-up model clarification as the first v3 patch release.
Approach: Promote the pending changelog fix to 3.0.1, advance its comparison links, and align the Cargo and NixOS module package versions.
Correctness: Cargo metadata and the flake package both report 3.0.1. The NixOS module parses and its static package version matches.
Excluded scope: This commit does not create or push the v3.0.1 tag, publish release assets, change runtime behavior, or bump the independently versioned grasp-audit crate.
Validation: git diff --check; cargo metadata --locked; nix eval of the ngit-grasp package version; nix-instantiate --parse of nix/module.nix plus an exact module-version check. Tests were not rerun for this metadata-only change.
The v3.0.1 authorization fix is intentionally small. Follow it with a separate structural pass so the implementation and documentation express the present-tense maintainer model directly instead of leaving the security behavior hidden behind owner-oriented names and repeated raw-tag interpretation.
Parse indexed roles once into a current-only snapshot of active maintainers, active lead targets, and announcement-author activity. Preserve detailed lead-resolution failures internally while policy callers continue to fail closed, distinguish selected authorization coordinates from physical owner views, and name broad announcement admission as discovery rather than authority.
Keep history relevant only while deriving current activity and retain active leads only for selected-coordinate resolution. Preserve the v3.0 public API through compatibility projections and deprecated aliases; this commit is not intended to change the authorization outcome established by 650cfb57.
Refresh architecture, inline authorization, storage, sync, and audit documentation. Correct the audit fixture description that claimed a listed maintainer authorized with no reciprocal announcement even though its setup already published one.
Validated with cargo test --lib (903 tests), cargo test --test state_authorization (53 tests), cargo test -p grasp-audit --lib (54 passed, 5 ignored), cargo test --test push_authorization (56 tests), and cargo clippy --tests -- -D warnings.
The v3 role-tag work filtered invited and ended listed maintainers, but every announcement author still seeded a repository view. A removed forwarding maintainer or an author with an ended self-role could therefore keep publishing authoritative kind 30618 state through their own coordinate. Parity-only role parsing could also treat malformed histories as active.
Validate numeric role boundaries and the final-end-only defer sentinel, resolve each selected coordinate through one valid active M path to a terminal self-M, and only then seed the reciprocal membership fixpoint. Legacy and explicitly leadless views continue to root at the selected author while that author remains active; incomplete, ambiguous, and cyclic explicit paths fail closed.
This changes current state and maintainer-scoped authorization only. Historical event authorization and membership mutation workflows remain outside the relay-side patch.
Validated with cargo test --lib (902 tests), cargo test --test state_authorization (53 tests), and cargo clippy --tests -- -D warnings.
Documentation builds need a source-owned view of ngit-grasp commands and runtime configuration without copying Clap help or operator defaults into another repository.
Add an early hidden __docs-export dispatch that serializes schema-v1 command metadata, deduplicated public environment options, runtime constraints, dotenv behavior, and relay-owner secret precedence. Clap remains authoritative for reflectable facts; a narrow explicit registry covers validation and the skipped secret.
Correctness assumes Config::validate relationships continue to be mirrored in the constraint registry when they change. Secret source metadata intentionally contains locations and precedence only, never loaded values.
This does not publish artifacts, generate ngit.dev pages, expose internal test switches, model conditional Clap requirements that stable reflection cannot expose, or change normal relay and maintenance-command behavior.
Validated with cargo fmt --check, targeted unit and startup-isolation integration tests, all-target/all-feature clippy with warnings denied, deterministic process-output comparison, and jq schema assertions.
The v3.0.0 source tree accidentally recorded a local linked worktree as a mode-160000 Gitlink without a matching .gitmodules entry. Git and Nix fetchers consequently materialized the same revision with inconsistent directory shapes and NAR hashes.
Remove only the index entry. The existing /worktrees ignore rule keeps repository-local linked worktrees out of future commits, and the registered worktree plus its branch and files remain intact.
This does not change runtime code, release behavior, or the linked worktree contents. Validation: git diff --cached --check before commit and git worktree list after removal.
Motivation: The v3 storage, security, private-repository, and proactive-sync changes have completed production migration and integrity-check validation.
Approach: Promote the accumulated changelog entries to 3.0.0, polish the major-release summary, advance comparison links, and align Cargo plus the NixOS module package versions.
Correctness: Cargo metadata and both Nix package evaluations report 3.0.0, matching the version required by the tag-triggered release workflow.
Excluded scope: This commit does not create the v3.0.0 tag, publish release assets, change runtime behavior, or modify deployment pins.
Validation: git diff --check; cargo metadata --locked; nix eval for ngit-grasp and static package versions. No build or tests were run for this metadata-only change.
Add portable deployment paths and managed hosting templates
nostr:nevent1qqsxdjfcdmr88n4pe79h4st43kvkwzssekuwepmsl7dt3ze2x0a27scpz3mhxue69uhhyetvv9ujumn8d96zuer9wc5fxfyx
PR-Author: DanConwayDev's Agent
nostr:npub1v47f74n2ycn66asev62nv8sas99akj0g0wg0fkup37u3ckwuzs4q7cwtp0
PR description:
Define one durable single-writer deployment contract, then implement it with a non-root Docker image, Compose and Caddy, a generic systemd unit, and separate NixOS, Linux, and Proxmox guides.
Add Railway, Render, and Fly.io templates that share the same /data layout, plus bounded public verification and a container replacement test that proves the relay identity survives.
The dedicated container e2e workflow expects the ngit-ci KVM runner to opt in to exposing its disposable guest Docker socket; it fails clearly if that prerequisite is absent.
The GRASP-06 rejection helper treated any 404 digits anywhere in git stderr as evidence that the smart-HTTP endpoint was absent. A randomly generated repository UUID containing 404b therefore turned a valid server-side ref rejection into a false test failure.
Match transport-specific Git missing-repository diagnostics and cover both the accepted phrases and the observed URL regression. This changes only audit classification; relay behavior and the GRASP-06 contract are deliberately unchanged.
Validated with the focused grasp-audit unit tests, cargo fmt --check, and the formerly failing grasp06_pr_hosting integration test against a spawned relay.
Motivation: Production v3 rehearsal found unmarked missing ancestors that the previous shallow-fetch wording could misattribute to a narrow pre-release bug.
Approach: Describe incomplete legacy object graphs without inferring provenance, retain the shallow marker as a specific historical case, and document a minimal maintainer-bundle recovery procedure. Remove stale test comments claiming current purgatory fetches are shallow.
Correctness: Current fetches have no depth limit, migration logged the affected graphs as pre-existing, and an unmarked missing parent is not evidence of the marked January fallback behavior.
Excluded scope: No integrity policy, object import automation, migration behavior, or production data is changed.
Validation: Ran git diff --check and reviewed the production sweep evidence; tests were intentionally skipped because this commit changes documentation and comments only.
The portable image and persistence test should run in CI rather than relying on an operator to have Docker installed locally.
Add a focused ngit-ci workflow that obtains only the Docker client, curl, and jq through Nix, then builds the image, replaces the relay container, and verifies that its NIP-11 identity survives on the mounted volume.
The workflow deliberately requires an explicitly mounted daemon socket and fails clearly when the operator has kept ngit-ci's secure default. This is intended for the disposable KVM guest daemon and does not weaken embedded-act hosts.
Validated the workflow as YAML and listed its jobs and triggers with act 0.2.86 or newer from the ngit-ci development shell; git diff --check passes. The actual container job requires the remote runner's opted-in guest socket.
The production guide was NixOS-only despite presenting itself as the general deployment entry point, and its examples referenced an unavailable GitHub source and a hardening control the module does not set.
Turn the entry point into an environment chooser, preserve the corrected NixOS material in its own guide, add a hardened generic systemd unit and repeatable Linux installation, document the preferred unprivileged Proxmox layout, and update repository navigation and architecture references.
Each path assumes the shared deployment contract from the container change. Kubernetes automation, remote host mutation, and changes to the existing NixOS module are deliberately excluded.
Validated the canonical Git remote with git ls-remote, parsed and scored the systemd unit with systemd-analyze, checked all new deployment-guide links, removed trailing whitespace, scanned the staged diff for key-shaped nsec values, and ran git diff --check.
Agents need repeatable provider inputs instead of translating a generic container guide into mutable dashboard settings on every deployment.
Add Railway, Render, and Fly.io configurations that all build the canonical Dockerfile, mount /data, expose one HTTP service, allow bounded shutdown, and avoid overlapping writers. Pair them with provider-specific CLI and dashboard instructions, DNS verification, backup cautions, and one-instance constraints.
The templates assume each provider terminates TLS and preserves WebSocket upgrades. Render still requires a supported connected Git source for Blueprint automation, and provider credentials or account mutations are deliberately outside this commit.
Validated Railway and Render against their current published JSON schemas, parsed both TOML files and all YAML, checked Fly fields against its current official reference, verified local documentation links, and ran git diff --check. flyctl strict validation was not possible without a Fly access token.
Production deployment previously depended on an illustrative Docker snippet and did not define which state or identity must survive replacement.
Add a non-root runtime image, loopback-only Compose service, optional Caddy TLS overlay, shared /data layout, bounded public verifier, and an identity-persistence container test. Document backup, proxy, single-writer, upgrade, and rollback requirements as the contract for every environment.
This assumes one ngit-grasp writer per state directory and a reverse proxy or platform edge for public TLS. Image publication and provider-specific control-plane setup are deliberately left to separate changes.
Validated with sh -n, ShellCheck 0.11.0, locked Cargo metadata, YAML parsing, Docker Hub tag lookups, local input-failure checks, and git diff --check. Docker/Podman is unavailable in this VM, so the included end-to-end container test was not run here.
Motivation: The previous wording could imply that signed Nostr repository state or normal Nostr Git checkouts were themselves compromised.
Approach: Describe the concrete risk as unauthorized server-side objects or refs, while distinguishing conforming Nostr Git clients from direct Git consumers.
Correctness: The wording retains the full v3 integrity-pass requirement and does not weaken the separate unauthenticated-read disclosure.
Excluded scope: No runtime behavior, checker policy, or release procedure changes are included.
Validation: Reviewed both rendered Markdown sources and ran git diff --check.
Motivation:
The authorization-integrity implementation now recognizes exact standard clone endpoints as an alternative owner-view authority, but the v3 changelog and migration architecture still described only maintainer-overlap propagation.
Approach:
Document both accepted owner-view paths and explicitly state that foreign services, different coordinates, URL suffixes, and /prs/ endpoints do not satisfy the standard endpoint rule. Preserve the existing GRASP-06 warning and operator requirement to inspect manual findings.
Correctness assumptions:
The accepted event database remains authoritative and GRASP-06 contributor views continue to use their separate signer plus exact /prs/ coordinate predicate.
Excluded scope:
This changes no runtime behavior, deployment pin, release tag, or production repository data.
Validation:
git diff --check; review against the remotely built and production-validated 44a16da9 behavior.
Motivation:
The authorization-integrity pass treated PR refs hosted in a submitter owner view as unexplained whenever the PR targeted a different repository owner. NIP-34 clone tags can legitimately name that submitter-hosted standard endpoint, producing false manual-inspection findings and obscuring actual unexplained refs.
Approach:
Authorize an owner-view PR ref when either the existing target-maintainer relationship applies or an HTTP(S) clone tag exactly names the canonical service mount, owner npub, and identifier. Include those source identifiers in the lease-time event refresh and reject foreign authorities, credentials, query/fragment suffixes, extra paths, different coordinates, and /prs/ endpoints.
Correctness assumptions:
Accepted PR and PR Update events remain the event authority. Exact clone URL ownership is an alternative location signal only for the named owner view; it does not weaken base-repository maintainer authorization or GRASP-06 /prs/ scoping.
Excluded scope:
This does not delete unresolved refs, repair malformed State HEAD tags, change storage migration, tag v3, or alter deployment configuration.
Validation:
cargo fmt --check; git diff --check. Compilation and production behavior will be validated through the required remote Nix deployments.
Motivation:
Production release-candidate validation must be able to exercise the new storage and event-authorization checker on selected identifier families without immediately sweeping thousands of repositories. The existing manual command also covered storage only, which made its name and operator workflow misleading.
Approach:
Apply one validated startup identifier scope to both background passes, with an empty scope retaining the secure all-family default and unmatched names counted as failures. Extend durable manual requests so check-only mode compares refs without mutation and --repair applies the same safe authorization reconciliation after storage repair. Expose the scope consistently through CLI/env, the NixOS module, examples, operator docs, architecture notes, and the v3 security warning.
Correctness assumptions:
Accepted State, PR, and PR Update events remain authoritative, active precisely-scoped purgatory entries remain valid in-flight exceptions, and unexplained PR refs remain preserved for manual inspection. A scoped pass proves only the named identifiers; full v3 assurance still requires removing the scope and completing the default sweep.
Excluded scope:
This does not tag v3, alter migration behavior, update the production deployment, or delete unexplained refs. It also does not make the manual request synchronous; the live worker continues to consume durable requests.
Validation:
- cargo clippy --all-targets --locked -- -D warnings
- cargo test --lib --locked (895 passed)
- focused scoped-selection and non-mutating reconciliation tests
- resource-safe NixOS module evaluation of startupIntegrityIdentifiers
Motivation: the v3 storage migration preserves legacy refs exactly, so structural Git integrity alone cannot detect refs written through the pre-v3 GRASP-06 path traversal. Operators need an online, post-migration answer without extending the production outage.
Approach: add a second startup pass that derives owner refs from the NIP-01-preferred State of the confirmed maintainer set and PR refs from accepted PR/PR Update events, including exact GRASP-06 and active-purgatory scoping. Fetch missing expected objects through the hardened repair path, repair unambiguous drift, and preserve unexplained refs with bounded manual-inspection logs.
Correctness: authoritative events are refreshed while holding the family lease, ref updates use compare-and-swap, and anything changed since the initial online snapshot is left untouched. This assumes the accepted event database and existing membership/GRASP-06 predicates are authoritative. The offline migration is deliberately unchanged, and unexplained PR refs are not auto-deleted because they may be evidence.
Validation: cargo fmt --all -- --check; cargo clippy --workspace --all-targets -- -D warnings; cargo test --locked; cargo test --lib --locked (892 passed after the final race guard).
The release checklist was requested as conversational guidance, and the checked-in version incorrectly treated an unauthorized GitHub repository as a project mirror.\n\nRemove the checklist and its index entries, along with the README claim that GitHub is a mirror. This assumes the Nostr-hosted repository remains sufficient for the current release; creating an authorized GitHub mirror is deliberately left for a separate decision.\n\nNo release tag, remote, package, container, deployment configuration, or in-progress v3 implementation is changed. Validation: git diff --cached --check and a scoped review of the staged documentation diff.
The release process spans canonical Nostr refs, ngit-ci artifacts, a stale GitHub mirror, an existing crates.io package, and native NixOS deployments, while the only public container belongs to the archived ngit-relay predecessor. Without one checklist it is easy to omit a live channel or publish into the wrong namespace.
Document release freezing, metadata synchronization, validation, immutable tag publication, artifact verification, GitHub reconciliation, the crates.io decision, production rollout, announcements, and rollback rules. Record the current mirror divergence and explicitly exclude Docker Hub and the archived GHCR image from the v3 release path.
The checklist assumes v3.0.0 remains the intended version, the canonical Nostr repository is authoritative, and production continues to consume the pinned NixOS module. It deliberately does not reconcile or push the GitHub mirror, publish any package, create a tag, add container packaging, or deploy production.
Validated with public GitHub, GHCR, Docker Hub, crates.io, and production-config inspection; a clean-worktree cargo publish dry run for ngit-grasp; staged markdown fence and relative-link checks; external link probes; and git diff --cached --check.
Version 3 changes maintainer authorization from unilateral listing to reciprocal membership and adds indexed NIP-34 role-tag parsing. Although the release-time authorization state of live repositories is unaffected, operators and clients need this called out before upgrading because future invitations require explicit acceptance and role tags supersede the legacy maintainers list.
Document the acceptance requirement, zero-live-repository impact, M/m/o semantics, and maintainers-tag fallback in Unreleased breaking changes. Also correct the existing invitation-sync entry so it no longer claims one-way authority before acceptance.
This assumes the reciprocal membership implementation, current architecture decision, and reported live-repository impact are authoritative. No runtime behavior or protocol parsing is changed.
Validation: git diff --cached --check
Tagged releases need installable artifacts whose source and embedded revision are pinned to the pushed tag, without requiring operators to have Rust or Nix on the deployment host.
Add a Linux pkgsStatic output and a v* ngit-ci workflow that verifies the tag against Cargo package metadata, builds the x86_64 MUSL binary, creates a reproducible licensed archive and SHA256SUMS, and uploads both as release assets. Derive the Nix package version from Cargo.toml so release validation has one authoritative version.
The first artifact target assumes x86_64 Linux and the existing tag-trigger environment provided by ngit-ci. Multi-architecture archives, OCI publication, release tagging, and the separate v3 metadata promotion are deliberately excluded.
Validated with a staged-tree nix build .#static, static PIE and embedded-revision inspection, an archive/checksum round trip, nix flake check --no-build --no-write-lock-file, cargo metadata, and git diff --check.
The README stopped at GRASP-02, described implemented Sync+ and object deduplication as roadmap work, and still compared the service with the retired ngit-relay project. This made the project overview and operator-facing settings materially diverge from the current implementation.
Present GRASP-02 and default-on GRASP-03 as one sync system, document opt-in GRASP-08 behavior and settings, replace stale sync options from the current CLI configuration, and move implemented observability out of the roadmap. Remove ngit-relay references and link the canonical ngit and ngit-ci sister repositories.
The descriptions and defaults assume src/config.rs, NIP-11 construction, and the current architecture documents are authoritative. This is documentation-only and deliberately does not change protocol behavior or configuration defaults.
Validation: git diff --cached --check; target/debug/ngit-grasp serve --help; canonical sister-project URLs returned HTTP 200
A generic mirror notice does not explicitly tell contributors where pull requests and issues are handled. Recast the banner as a canonical-repository notice and state that collaboration is tracked exclusively through ngit and Nostr at Git Workshop.
This wording assumes GitHub remains a mirror but is not an active issue or pull-request tracker. No repository configuration or contribution tooling is changed.
Validation: git diff --cached --check