How we set groundinglab's admission policy
groundinglab's admission gate lets a PCOC in when its making kept a steady pace, enough of its points are anchored in Bitcoin, and a check of one randomly placed leg in each stage matches. The count in the table is this policy's live setting, not a rule of the specification. Below are the live numbers, the reasoning behind the calibration study, and the evidence, which anyone can check and rerun.
25 October 2026 (ACDT). Live policy: groundinglab admission policy gl-admit-v12, status active. Born height is 972685. Heights below 972685 are testing, not production. An earlier object is rejected with before_born_height. The gate runs it under SPEC 2.3 (pin spec-2.3.0). Live testing floor is 0.1 legs/s (1/10). There is no tip-versus-last-beacon submission deadline, and verify_cap_per_door_session is unset (no door-session cap). Specification 2.3 folds errata 1 and 2 into the ratified text and gives no lever a default.
The policy
These are the settings groundinglab's own gate uses. The specification defines each one, its unit and its allowed range; a policy picks values inside those ranges. Version 2.3 gives no lever a default. They are groundinglab's choices, not rules of the specification, and another service's gate can choose differently.
| Setting | Value | What it means | Allowed by the specification |
|---|---|---|---|
| Leg-rate floor | 0.1 legs per second (1/10), testing | At least one leg every 10 seconds on average, over the making, measured against Bitcoin's block times. Live testing floor; retune after more produces | any whole-number ratio p/q |
| Deep check | 18 legs, in 3 rounds of 6 | 18 randomly drawn legs are recomputed in full | at least 1 round of 6 |
| Anchored minimum | 6 of 16 points | At least 6 of the 16 recorded points are anchored in Bitcoin, any 6 | 2 to 16 |
| Submission deadline | none | No tip-versus-last-beacon deadline. SPEC 2.3 defines no such lever; this policy does not set one | — |
| Door-session verify cap | unset | No per-door-session verify cap. A valid PCOC is judged on merit, not on how many times this door verified | integer ≥ 1, or unset |
| Held retry | 600 seconds | A check that could not read Bitcoin's record is retried every 10 minutes | at least 1 second |
| Fake leg | no expiry | A key caught with a fake leg is ignored at this gate from then on | any time, or no expiry |
The full policy file and its notes: policy/groundinglab-admission on GitHub (gl-admit-v12.toml).
The live testing floor is 0.1 legs per second (1/10). The 0.2 calibration in the argument and speed tables below is the measurement study that argued for a production floor candidate; it is labelled as that study and is not the live lever.
The argument
- What the floor is for. A PCOC is worth something because making one costs real resources over real time. The floor is the slowest pace the gate accepts. Together with the memory each PCOC needs, it caps how many PCOCs one machine can make at once, and that cap keeps PCOCs scarce. See the threat model and cost and incidence.
- One honest maker passes easily, even on modest hardware. Making one PCOC, our three commodity reference systems (two laptops and a mini PC) run at 0.402, 0.375 and 0.242 legs per second (table below). The slowest is reference system 2, the low-end machine the specification names. Even reference system 2 is about 20% above the 0.2 calibration study floor (and well above the live 0.1 testing floor).
- Making many at once runs into memory bandwidth. Each PCOC needs its own 256 MiB of memory, and no cache on these machines holds it. On the two six-core laptops the total levels off at about 1.0 to 1.14 legs per second, however many PCOCs are added, so each one gets slower. At four at once both laptops stay above the 0.2 calibration study floor. At six at once both fall below it, at 0.190 and 0.170. Reference system 2 stays above that study floor making two at once, at 0.238. At a ceiling of 1.14 and a study floor of 0.2, a machine like reference system 1 can make at most 5 PCOCs at once and still pass that study bar (derived).
- Optimising is welcome, because the work still gets done. Vector instructions, the processor's SHA instructions and running real PCOCs side by side all fulfil the contract. The cost holds because every leg is really computed. Six builds in six languages land within the same range on each machine, set by how fast it moves memory, and none found a way to skip work and still pass.
- Faking legs is cheating, and it doesn't pay. Each leg starts from the previous one's result, so the chain is sequential, and its pace is measured against Bitcoin blocks that no one can predict. The 18 legs are drawn after the PCOC is submitted. Faking a quarter of the legs would gain at best one extra PCOC for every three, and is caught 99.44% of the time. Faking 1% is caught about one time in six but saves only about 1%, and one catch means the gate ignores that key from then on (odds below). In our judgment the trade loses at every fake fraction.
- The other settings are operational. Six anchored points are enough to time the making without punishing a weak connection. This live policy sets no tip-versus-last-beacon submission deadline. A ten-minute retry waits out a temporary outage without hammering Bitcoin's record. Version 2.3 gives these levers no default. The values in the table are this policy's live settings.
- The verifier measures; the policy decides. Every gate measures a PCOC the same way. This page argues for groundinglab's choices, and a service with other needs can choose other values inside the same ranges. See How it works and the specification, sections 15.4 and 15.5.
Calibration study (not the live lever): a 0.2 production-floor candidate was calibrated on reference system 2 (low-end), with reference system 1 as the primary reference. The live testing floor is 0.1 (1/10). The full derivation of the 0.2 study is in the calibration note.
What this is, and isn't
This is not a claim of proof; it is a judgment call based on the evidence we have so far.
More research and results will follow, including SDK optimisation and refinements to the specification. PCOC is an evolving, flexible design. Its settings can be tuned for different contexts, environments and cost floors, and any new value will come with a new policy id and new evidence.
How we tested
- One specification, nothing else. Each builder got only the public specification (2.2 with erratum 1), the policy files and a fixtures list naming the cases and output keys, with no expected values. Everything was pinned to one commit.
- One prompt. Every builder got the same published prompt, with only the language changed. It asked them to optimise as far as their language allows, and to report any way to do less work than the specification intends as a possible flaw.
- Isolation. Each build was written by an AI model working on its own, in a new empty repository, with no access to any other implementation.
- Six models, six providers, six languages, all at high effort (table below).
- Reference systems. Code can be built anywhere, by anyone. The timings that count were taken by compiling each build from its own source and running it on three fixed reference systems, all commodity hardware (two laptops and a mini PC): reference system 1 (primary), reference system 2 (the specification's low-end host) and reference system 3. Their configurations are in the table below and in REFERENCE-SYSTEMS.md. The builders' own cloud runs only showed that the builds work, and are not used.
- A first round. Two builds at low effort, on purpose, to expose ambiguity. Their disagreements became the 29 clarifications of erratum 1 (round 1).
| Build | Model | Provider | Language | Effort | Commit |
|---|---|---|---|---|---|
| A | Claude Sonnet 5.5 | Anthropic | TypeScript, Node 22 with WebAssembly | high | b3d7b02 |
| B | GPT-5.6 Sol | OpenAI | Python 3 with a C extension | high (no separate effort setting was exposed to it) | b18a5f6 |
| C | Grok 4.7 | xAI | Zig 0.15.2 | high | ffcfc22 |
| D | Gemini 4 Argon | Kotlin, JVM 21 | high | fd24932 | |
| E | Kimi K3 | Moonshot | C#, .NET 8 | high | d9fa6ea |
| F | GLM 5p3 | Zhipu | Swift 6.1 with C kernels | high | 6d976f4 |
Each build's code, prompt, model card and its own unedited report are on GitHub.
Six builds agree
- Same bytes. On all three machines, all six builds produce byte-identical test objects: O1
800ad07a87a567c8…, O26bfc769b831b62de…and the production object O3b16ecdaa13ad6df2…. - Every build verifies every other build. 6 verifiers × 6 producers × 3 objects × 3 machines: 324 of 324 cross-verify cells match.
- Negatives refused. Every build reports all 13 permanent negatives, the known wrong shortcuts, as non-matches, on every machine.
- Fixtures. 13 of 14 fixture outputs are identical across all six builds. The exception is one test vector in build F (
hashcash.txt): it hashes a header once where the fixtures ask for twice. It touches no object and no verify record.
| Object | Ref. system 1 | Ref. system 2 | Ref. system 3 |
|---|---|---|---|
| O1 (small test profile) | 36 of 36 match | 36 of 36 match | 36 of 36 match |
| O2 (larger test profile) | 36 of 36 match | 36 of 36 match | 36 of 36 match |
| O3 (production profile) | 36 of 36 match | 36 of 36 match | 36 of 36 match |
Every cell is listed in verify-grid.csv. O3 records differ only where build C draws its six spot-check legs from the operating system's random source; all its results match.
How fast, on reference systems
| System | Role | CPU | Cores / threads | L3 cache | SHA instructions |
|---|---|---|---|---|---|
| Reference system 1 | primary reference | AMD Ryzen 5 5600H, laptop | 6 / 12 | 16 MiB | yes |
| Reference system 2 | the specification's low-end host | Intel Core i7-10610U, mini PC | 4 / 8 | 8 MiB | no |
| Reference system 3 | second commodity laptop | AMD Ryzen 5 7430U, laptop | 6 / 12 | 16 MiB | yes |
One production leg (2,000,000 steps), seconds, median of 3 runs, optimised / plain scalar (measured):
| Build | Ref. system 1 | Ref. system 3 | Ref. system 2 |
|---|---|---|---|
| A TypeScript | 6.32 / 139.44 | 6.56 / 133.81 | 9.47 / 183.77 |
| B Python + C | 5.74 / 5.73 | 5.85 / 5.75 | 7.92 / 7.91 |
| C Zig | 2.47 / 2.51 | 2.72 / 2.72 | 7.59 / 4.21 |
| D Kotlin | 2.75 / 6.11 | 2.95 / 6.09 | 5.16 / 9.52 |
| E C# | 3.94 / 6.22 | 4.03 / 6.14 | 6.34 / 8.77 |
| F Swift + C | 3.17 / 4.85 | 3.27 / 5.04 | 5.04 / 7.80 |
Making several at once (measured): N independent PCOCs on one machine, each with its own key and its own 256 MiB of memory, nothing shared and nothing skipped. Build C was the producer: its fast engine, except on reference system 2, where its scalar engine is the faster one. The last counted leg of every instance was recomputed by build D, and every one matched.
| Machine | At once | Each, legs/s (median / slowest) | All together, legs/s | Above the 0.2 study floor? |
|---|---|---|---|---|
| Reference system 1 | 1 | 0.402 / 0.402 | 0.402 | yes |
| Reference system 1 | 4 | 0.283 / 0.282 | 1.133 | yes |
| Reference system 1 | 6 | 0.190 / 0.184 | 1.140 | no |
| Reference system 3 | 1 | 0.375 / 0.375 | 0.375 | yes |
| Reference system 3 | 4 | 0.246 / 0.242 | 0.985 | yes |
| Reference system 3 | 6 | 0.170 / 0.163 | 1.015 | no |
| Reference system 2 | 1 | 0.242 / 0.242 | 0.242 | yes |
| Reference system 2 | 2 | 0.238 / 0.238 | 0.477 | yes |
Method: all instances start together. The memory fill and first leg are discarded, then a 60-second window is measured with every instance running. These are steady leg rates over that window, not yet the full rate over a real hike paced by Bitcoin blocks (still open). Load: reference system 3 had a browser open; on reference system 2, a batch job was paused during these runs.
How the floor follows (derived, then judgment):
- The specification says to set the floor a little below the low-end host's best legal rate. Reference system 2 runs 0.242 alone and 0.238 each with two at once. 0.2 is about 17% below its single rate.
- On the six-core laptops the floor falls between four and six at once. At the measured ceiling that means at most 5 per machine.
- So the calibration study kept 0.2 legs per second as the production-floor candidate. The live testing floor is 0.1 (1/10).
What each language could do
- TypeScript (A). Node gives no access to the SHA instructions, so it relies on a WebAssembly kernel. It is the slowest optimised build, and its plain version is about 20 times slower again.
- Python with C (B). Its only fast path needs AVX-512, which none of the reference systems has. There, its optimised and plain results are the same compiled C.
- Zig (C). Its explicit vectors give the fastest leg on the machines with SHA instructions. On reference system 2 its fast path is slower than its plain one, which is untuned and flagged in its review.
- Kotlin (D). The JVM's compiler vectorises the loop on its own. Its 512-bit vector path is emulated on these machines and is not used.
- C# (E). It uses an AVX2 engine. .NET has no direct SHA instructions, so it calls the system's crypto library for the hashing.
- Swift with C (F). It uses C kernels with an AVX2 path.
Every build that reached the processor's vector and SHA features lands within about 2.5 to 4 seconds per leg on the Ryzen laptops. The limit is memory bandwidth, not the language, and no language gives a way below the floor.
Attacks we considered
Faking legs. A fake leg is one whose result was never computed. The deep check recomputes 18 legs drawn at random after submission. The chance of being caught when a fraction f of legs is faked is 1 − (1 − f)18 (specification section 15.5):
| Share of legs faked | Work saved, at best | Caught |
|---|---|---|
| 1% | about 1% | 16.5% |
| 5% | about 5% | 60.3% |
| 10% | about 10% | 85.0% |
| 25% | about 25% (at best one extra PCOC for every three) | 99.44% |
| 50% | about 50% | 99.9996% |
- Sequential chaining. Each leg starts from the previous leg's result, so a fake cannot be patched in after the fact.
- Unpredictable timing. The pace is measured against Bitcoin's block times, which no one controls, so a saving can't be banked ahead.
- The cost of a catch. The PCOC is refused, and that gate ignores the key from then on.
- Known shortcuts. The specification lists 13 permanent negatives, ways to do less work that must give a different answer. All six builds rejected all 13.
- Optimising is not cheating. Doing every leg faster, or running several real PCOCs side by side, fulfils the contract. Faking skips the work.
Still open
- Confirm the floor with full hikes paced by real Bitcoin blocks, on reference system 2 (low-end) and reference system 1. The figures above are steady 60-second windows. This is open, not blocking.
- Publish the policy under its final id and deploy it on the gate.
- Confirm four operational settings carried over from the earlier policy: retry count, held-block limit, fast-accept bound and the background check budget.
- The next specification revision (2.3), now queued.
- SDK optimisation.
- More machines, especially ones that you test.
Test it yourself
This is an open test, and we'd like more people to run it. Clone the evidence, build any of the six, or write your own from the specification, then run the same kit on whatever you have: a laptop, a server, an ARM board. Compare your numbers with the tables above and send them to us. A different machine or a better build is exactly the evidence we want.
- Get the code:
git clone https://github.com/groundinglab/pcoc, then follow the evidence README. The kit installs every toolchain in your user directory, with no root needed. - Build, test and cross-verify. Your objects should match the published SHA-256 sums exactly.
- Time a leg and a few PCOCs at once.
- Submit through a Reference results issue, or a pull request in the results format. See how to submit.
Submissions are listed as community-measured, kept apart from our reference runs, and considered at the next calibration.
Read more and sources
- How it works: making, checking and anchoring a PCOC.
- Threat model: the two ways it can fail.
- Cost and incidence: who pays, and why the check costs less than the making.
- What does a PCOC cost at scale?: cost per identifier when renting servers.
- The specification.
- Evidence on GitHub: spec/conformance/interop, including round-2 results and raw logs, round 1 and the floor calibration.
- Live policy file on main: gl-admit-v12.toml (served as
gl-admit-v12on GET /v1/policy). Evidence: spec/conformance/interop. The pack: spec/conformance/pack. SPEC pin spec-2.3.0.
If you think a figure is wrong, rerun it and tell us.