clone

a self-hosted sync node for lofi apps: jazz sync over iroh, ticket-gated, with no static ip or cloud needed

chore(ci): run the gate on src.rest with the native addon built from source

cat committed b2c134a
forge attest runs .forge/ci in a clean worktree of each commit and signs the
result into refs/notes/ci; forge push sends it with the branch. It runs
deno task check, builds native/iroh-js with cargo into ~/.cache/lofi-node, and
runs deno task test against that build, so the iroh and jazz integration tests
exercise this commit's Rust source rather than the committed prebuilt.
commit
b2c134a28358c7432596777812179114190d6eba
parent
9bc3319fe30d747921f27206ba6d5a5193f29894
author
cat <admin@dev.site>
ci
✓ passed in 67s, signed by cat
ci attestation
forge-attestation 1
type ci
commit b2c134a28358c7432596777812179114190d6eba
tree 93d5811127b53b26c6b54528d1759f72e366f1b6
script .forge/ci 0685aa6762a77ce6c1210b6e0a89252153138fca
result pass
exit 0
started 2026-10-17T22:59:35Z
duration 67s
runner (not shown here; refs/notes/ci has it)
system Darwin 25.5.0 arm64
env host
log sha256:feeebe56e1d7616ebc51bb0c9585159013fa5261952f83c92548d279c188b0dd 518 lines
|gate: store-status is metadata-only and any-scope ... ok (1ms)
|running 4 tests from ./tests/health_test.ts
|health: open mode — Jazz answers on the public URL ... ok (65ms)
|health: ticket mode — unauthenticated at the top level, and under a ticket base ... ok (58ms)
|health: gate degrades to 502 when the store is unreachable ... ok (1ms)
|health: peer mode — carried over the iroh tunnel to the node's Jazz ... ok (1s)
|running 1 test from ./tests/mesh_test.ts
|two in-memory JazzServers boot and chain upstream ... ok (118ms)
|running 3 tests from ./tests/pair_test.ts
|two nodes pair over iroh: leaf upstream tunnels to root ... ok (3s)
|runtime pair(): late upstream election without losing the port ... ok (3s)
|ticket mode: gate URL and port survive runtime pair() ... ok (1s)
|running 6 tests from ./tests/pop_test.ts
|derive binds a device key; deriving without one stays bearer ... ok (30ms)
|a malformed device key is a 400, not an auth failure ... ok (1ms)
|a bound ticket refuses bare paths and honors the full exchange ... ok (6ms)
|wrong keys, consumed challenges, and foreign tokens refuse as one shape ... ok (5ms)
|revoking the parent kills tokens and closes bound sockets ... ok (2s)
|the cross-repo signature fixtures verify ... ok (1ms)
|running 2 tests from ./tests/provisioning_test.ts
|provisioning: slice A → union merge with createTables, all through the gate ... ok (170ms)
|provisioning: through an iroh-paired leaf lands on the root ... ok (1s)
|running 6 tests from ./tests/relay_config_test.ts
|relay: absent from new configs (n0 default) and round-trips when set ... ok (10ms)
|relay: malformed elections are rejected with the offending value ... ok (706µs)
|relay: a config.json with a bad relay fails at load, not at dial time ... ok (825µs)
|relay: createSyncNode rejects a bad election before any boot work ... ok (257µs)
|relay: disabled endpoints still pair over direct addresses ... ok (13ms)
|relay: a custom relay map binds cleanly (no dial attempt at boot) ... ok (4ms)
|running 4 tests from ./tests/storage_config_test.ts
|storage: user-chosen sqlite path is honored on disk ... ok (71ms)
|storage: unwritable path fails fast with the path named ... ok (645µs)
|config: v1 file migrates to v2 with open access + sqlite storage ... ok (1ms)
|config: new inits default to ticket access ... ok (1ms)
|running 2 tests from ./tests/tunnel_iroh_test.ts
|ws echo through the iroh tunnel (dialer ws → iroh → acceptor ws) ... ok (2s)
|release artifacts manifest matches deno.json version and prebuilt digests ... ok (390µs)
|running 7 tests from ./tests/verdict_test.ts
|verdict: permission_denied is permanent ... ok (308µs)
|verdict: transaction_conflict is permanent ... ok (22µs)
|verdict: permissions_head_missing is permanent ... ok (30µs)
|verdict: expired is permanent ... ok (16µs)
|verdict: invalid_batch_submission stays unregistered (transient) ... ok (24µs)
|verdict: unknown codes classify transient ... ok (49µs)
|verdict: the registry only holds valid classes ... ok (39µs)
|running 2 tests from ./tests/write_conflict_test.ts
|write verdict: a losing concurrent transaction settles transaction_conflict, final and deterministic ... ok (174ms)
|write verdict: a store without a permissions head rejects writes with permissions_head_missing ... ok (376ms)
|running 1 test from ./tests/write_verdict_test.ts
|write verdict: rejection correlates by batch id, survives reconnect, and is deterministic ...
|------- post-test output -------
|WARN crates/jazz-tools/src/sync_manager/inbox.rs:1401 error from server server_id = ServerId(01a14c18-92ce-73e0-8da1-e18b868f1766);
|error = PermissionDenied { object_id: ObjectId(01a14c18-91c6-7c51-8675-5368fc0d1c17), branch_name: BranchName("dev-75fe0b3ed57f-main"), code: "permission_denied", reason: "Insert denied on table verdict__entries - missing explicit policy" };
|WARN crates/jazz-tools/src/sync_manager/inbox.rs:1401 error from server server_id = ServerId(01a14c18-92ce-73e0-8da1-e18b868f1766);
|error = PermissionDenied { object_id: ObjectId(01a14c18-91c8-7ca3-83e2-6afd8f1e2461), branch_name: BranchName("dev-75fe0b3ed57f-main"), code: "permission_denied", reason: "Insert denied on table verdict__entries - missing explicit policy" };
|----- post-test output end -----
|write verdict: rejection correlates by batch id, survives reconnect, and is deterministic ... ok (1s)
|
|ok | 74 passed | 0 failed (27s)
|

Signature: good, cat

1 file changed, +16 -0
A.forge/ci16++++++++++++++++
A .forge/ci +16 -0