Integrate a producer

This page is for developers who want their own app to make PCOCs. A producer is the part of an app that makes a PCOC on the user's device, from the user's own key. Start with the SDK in your language: the Rust, Go and Java SDKs all produce and verify, and each has a runnable produce example. The desktop app is a complete example of that path. The Primal Android demonstrator builds on the Rust SDK tag sdk-rust-v2.3.1. The specification and the details below are there for when you need more.

Examples

Assumes: the specification pack. The produce program's flags are in each SDK's --help (SDKs).

Minimal checklist

  1. Public key

    A public key (x-only) for the object.

  2. Bari URL and gossip peer

    A Bari URL and a gossip peer (or live DNS-seed fallback).

  3. Run produce

    Run produce until the final block arrives; write the CBOR object.

  4. Verify the object

    Verify the PCOC against an index; keep the §14 record for your policy.

Specification detail

  1. Output shape

    Output. A producer is the program that makes a PCOC. Its output is a PCOC of kind v2.2-pcoc-object: 18 stages, deterministic CBOR (SPEC §7.3, §8 Rule A3). STEPS_PER_LEG 2000000, SAMPLE_BYTES 1024, checkpoint preimage 1096 bytes (SPEC §6). A verifier records any other kind as an unknown kind (Rule A5).

  2. Inputs and keys

    Inputs. (a) A tip sequence of Bitcoin headers. It is the public sequencer that paces the stages (sdk/README.md; SPEC §9).

    (b) A Bari, a service that timestamps the producer's waypoints (32-byte values recorded at each stage) against Bitcoin. It must accept paid waypoints and serve Merkle paths (SPEC §13.6).

    A paid waypoint is POST /waypoint with a Hashcash stamp of 21 leading zero bits: the nonce in X-Bari-Hashcash-Nonce and the 80-byte fee header in X-Bari-Hashcash-Header, solved on Bari's current block, the one before it or the one after it (Rule BR1). Paths are GET /path/{leaf} (Rule BR4; services/README.md). (c) The producer's x-only public key (--pubkey-hex).

    Keys. nip06 holds demo keys only and isn't the seed (sdk/rust/README.md). The 64 bytes public_key ‖ seed_beacon are the seed preimage. The seed is SHA-256 of those bytes, 32 bytes with no label, read as four little-endian lanes (SPEC §10, P10.1; §4 glossary "Seed preimage").

  3. Endpoints (opt-in)

    Endpoints, all opt-in. The hosted Bari is https://bari.groundinglab.io. To run your own: bari with BARI_TIP_SOURCE=bitcoin-gossip, your own peer (PCOC_LIVE_GOSSIP_PEER) and Bitcoin Core RPC plus a wallet for the plant, or synth for the test plane (services/bari/README.md, deploy/env.example).

    If you run your own Bari, the plant-fee settings are operator settings in env.example, not rules of the specification.

    A plant (the Bitcoin transaction output that timestamps a batch of waypoints) is a 39-byte tag: OP_RETURN ‖ push-37 ‖ BARI ‖ 0x02 ‖ 32-byte root (services/index/README.md). An app that uses a Bari does not fund the plant transactions itself. For the initial release, plants are identified by that tag alone; operator-key attribution is planned and optional (spec/03-Waypoint_Publication_Process.md).

  4. Procedure at a high level

    The procedure at a high level. The seed beacon is the latest header already known at start, with no wait (SPEC §9). Each stage runs its legs back to back (every step reads a full 16 KiB slot of a 256 MiB terrain), then its checkpoint (§11), then a waypoint (§12.2). Nothing pauses a leg (Rule A6): the waypoint post, the Hashcash solve, path asks, peer and header reads and log writes all run alongside the legs. There is no wait inside the making.

    A post that gets no HTTP status line, or a 502, 503 or 504, is a transitory send failure: the producer resends the identical post once, in the background, 30 seconds later, then drops that waypoint if the resend fails too (P13.13). No other answer is retried.

    After each header in the anchor window, offsets 2, 3, 4 (§13.1), the producer asks for the paths of W_1…W_16; W_17 is posted but never asked. Production closes as soon as the final block is observed (§13.2). Paths not yet fetched at the close are recorded as skips, so the last stages are unanchored by design (§13.3). Reorgs and clock reads that fail never stop a run; they are logged. Event order at stage boundaries follows Rule A1 (§13).

  5. Integration routes

    Integration routes. Shell out to the SDK pcoc command (its --help; sdk/README.md), or call the SDK's produce function from your own code, as the Rust command does. The Android demonstrator links the Rust SDK over JNI. The current build guide is the primal-pcoc README. apps/android/README.md records the earlier vendored build.

  6. Restart and log

    Restart and log. Bari restart is §13.4 (Informative): replacing a live Bari during produce is normal, a pooled waypoint survives a quick restart, and the worst case is a skip, not a replaced plant. The live event log is §13.5 (Normative): live profile only, an append-only file at the object path plus .events, one compact JSON line per record (block, waypoint, posted, checked, fetch, alert). Each post attempt, including the one resend, is its own posted record. Every append is asynchronous. The log is not part of the object or an input to any leg, and a failed write leaves the PCOC unchanged.

  7. Conformance

    Conformance. Byte-equality targets are in the single conformance pack, spec/conformance/pack. The pack status is complete. Its manifest SHA-256 is 91ded57e5c05b7cb4cd74d313a51d83d2373fa86dc625df41faed9586e9032af. The README names the vectors. A §13.2 producer replay must equal the pack's object bytes. Produce does no signature check (SPEC §2). Signing happens at sign-in (Sign-in wire). Next: check what you made (Verifier and policy).

Checked against pcoc main at 7b40353. Android SDK pin and the produce command name checked against primal-pcoc 3.5.50 (commit 5a1d4f5) and pcoc main at 797c1ac. Audit date for that Android pin: 25 Oct 2026. Raw markdown.