Skip to content
PatchArc

Inside a .parc: canonical JSON, Ed25519, and seven verification checks

A walk through the capsule format as patcharc 0.2.0 writes it: the ZIP layout, why it is stored uncompressed, what the manifest signs, and each check verify performs on your machine and again in the cloud.

PatchArc4 min read

A .parc file is boring on purpose. It is a ZIP you can open with unzip, a JSON manifest you can read with jq, and a signature you can check with any Ed25519 library. This post walks through what 0.2.0 actually writes and how verification works, so you can decide whether to trust it without trusting us.

Layout

Entries are written in a fixed order: manifest.json, manifest.sig, manifest.pub, then data files sorted by path.

$ unzip -l arc_8f2c1d4a9b.parc
manifest.json
manifest.sig
manifest.pub
evidence/file_changes.json
evidence/git/commits/a1b2c3d….json
evidence/git/diffs/a1b2c3d….patch
evidence/scopes/index.json
evidence/summary/review.json
evidence/test_runs.json
manifest/arc.json
manifest/redaction.json
manifest/trust.json

Everything is stored with ZIP method 0 (no compression). That is a deliberate choice: the cloud verifier is a small reader running in a Worker, and uncompressed entries let it read sizes from local file headers without inflating anything. Capsules are small enough (tens to hundreds of kilobytes) that compression buys little.

The manifest

The manifest is canonical JSON: keys sorted, no whitespace. It records the format name and version (parc, 0.2.0), schema version, arc and session ids, the repository's heads before and after, the worktree, totals, a trust block, and the file table: every data entry with its size, SHA-256, and a kind (summary, git, scope, metadata, and so on).

Canonicalisation is what makes the signature robust: verification re-serialises the parsed manifest the same way before checking, so reformatting cannot break a capsule and changing a value cannot survive.

The signature

patcharc init generates an Ed25519 key pair per repository and stores the private key hex-encoded at .patcharc/keys/arc-signing.ed25519 with mode 0600 (and the directory 0700), and adds it to .gitignore. At seal, the canonical manifest bytes are signed; the 64-byte signature goes in manifest.sig and the 32-byte public key, base64, in manifest.pub. The key travels with the capsule, so verification needs nothing external. The key is not bound to an identity; identity comes from the cloud account that uploads, and the trust model is explicit about that.

Seven checks

patcharc verify runs these in order and reports every issue it finds:

  1. Path safety. No absolute entry names, no .., no ./ prefix, no NUL bytes.
  2. Compression ratio. Any entry whose uncompressed size exceeds 1000 times its compressed size fails. With method 0 this is a guard against a hostile re-packed archive.
  3. Presence of the three manifest entries.
  4. Manifest validity. Format name, exact version match, schema version at least 1, id patterns, non-empty heads and default branch, 64-hex SHA-256 on every file.
  5. Signature over the re-canonicalised manifest with the embedded public key.
  6. Every listed file re-hashed and size-checked; any archive entry not in the manifest is an issue.
  7. Completeness. Every listed file is present.

The result is valid only if all pass with zero issues. inspect prints the same checks as a table. The exit code is 1 on failure, which is what makes verify usable as a CI gate.

The cloud does it again

On share, the upload is first refused unless the body's SHA-256 matches what the CLI declared when it opened the session. Then a per-capsule Durable Object runs an independent TypeScript implementation of checks 3 through 6 (workers/src/util/parc.ts). Only then is the capsule marked published and a link created. A capsule that fails is marked failed_terminal and never served.

Verify without PatchArc

# 1. canonicalise the manifest (sorted keys, no whitespace)
$ unzip -p c.parc manifest.json | python3 -c 'import json,sys; print(json.dumps(json.load(sys.stdin), sort_keys=True, separators=(",",":")), end="")' > canon.json
# 2. check the signature with any Ed25519 library using manifest.pub
# 3. re-hash each listed file
$ unzip -p c.parc evidence/summary/review.json | shasum -a 256

What the spec promises that 0.2.0 does not write

The format specification reserves entries for symbols, decisions and risks as separate files, checkpoints, artifacts, an audit log, and an attestation. 0.2.0 writes the entries listed at the top of this post and no more; decisions and risks live inside the review, and test runs are an empty file. We would rather document the gap than ship empty directories.

engineeringparced25519formatverification