Turn Liber Primus runes into scored, reproducible cryptanalysis.
Parcae is a C++20 toolkit for classical cipher search over Cicada 3301's 29-symbol alphabet. It freezes solved pages as regression oracles, ships deterministic CLIs (optional CUDA twins), and compiles Python-authored theories into verified artifacts -- so you (or an LLM agent) can search what remains unsolved without inventing Z29 math.
Who this is for: researchers, engineers, and agent builders who want fixture-grade evidence on Liber Primus (or any mod-29 / classical puzzle) -- not another cipher notebook full of vibes.
| License | MIT |
| Toolkit version | 1.0.0 (v1.0.0) |
| Language (core) | C++20 (header-first library + CLIs) |
| GPU | Optional CUDA twins (CI stays CPU-only) |
| Python | IDE stubs + AST dump + optional CMD agent -- not the crypto core |
Start here: 5-minute tutorial / Theory DSL examples / Docs map
Goal: decrypt a known Liber Primus page, then compile a real @Theory.
If every command below exits 0, you are done.
Fastest (release installer): download a CPU package from GitHub Releases
(see docs/architecture/release.md). The
Windows .exe installs CLIs + data/ and puts tools on PATH.
From source (C++20 compiler, CMake >= 3.25; first configure needs network for FetchContent):
cmake -S . -B build -DPARCAE_BUILD_TOOLS=ON -DPARCAE_BUILD_TESTS=ON
cmake --build build --config ReleaseBinaries:
- Visual Studio / multi-config:
build/tools/Release/ - Ninja / Make:
build/tools/
# bash / Git Bash
export BIN=build/tools/Release # or build/tools
export PARCAE_DATA_DIR="$PWD/data"# PowerShell
$BIN = "build/tools/Release" # or build/tools
$env:PARCAE_DATA_DIR = "$PWD/data"$BIN/parcae-validate --data-dir data --id a-warning --require-lockedExpect PASS a-warning and exit 0.
$BIN/parcae-decode --data-dir data \
--manifest data/fixtures/solved/a-warning --jsonresult.latin should start with AWARNINGBELIEUE... (Atbash over the frozen
Gematria Primus alphabet).
$BIN/parcae-generate --data-dir data --generator-id gen_atbash \
--input data/fixtures/solved/a-warning/ciphertext.txt --runes --json \
| $BIN/parcae-rank --data-dir data --candidates - \
--score-id chi2_english_gp_v0 --k 1 --jsonTop hit candidate_id is atbash; Latin again starts with AWARNING....
Theories are not ordinary Python programs. They are a restricted DSL that
looks like Python: only from parcae.dsl.* import ... is allowed (no numpy,
no stdlib, no bare import). Decorators (@Theory, @ComposedTheory,
@define_primitive) mark structure for parcae-compile. IDE stubs autocomplete
but refuse to run crypto -- only the C++ compiler verifies and emits.
Optional stubs for editor help:
pip install -e ./pythonfrom parcae.dsl.math import Z29Expr
from parcae.dsl.theory import Theory, Param
@Theory(
name="hello_shift",
family="elementwise",
tier="A",
)
class HelloShift:
shift: Param[int] = Param(min=0, max=28)
def encrypt_step(self, x: Z29Expr) -> Z29Expr:
return x + self.shift
def decrypt_step(self, x: Z29Expr) -> Z29Expr:
return x - self.shiftfrom parcae.dsl.theory import ComposedTheory, Param
@ComposedTheory(
name="hello_atbash_caesar",
steps=["atbash", "caesar"],
tier="A",
)
class HelloAtbashCaesar:
caesar_shift: Param[int] = Param(min=0, max=28)
def step_params(self) -> dict:
return {"atbash": {}, "caesar": {"shift": self.caesar_shift}}Matrix / autokey builtins (z29_matmul, z29_det, compose leaves matrix_mix /
autokey_lag): see
theories/examples/matrix_builtins_example.py
and docs/spec/dsl.md § Core math ops. Specs for Hill /
autokey catalog transforms: docs/spec/transforms.md.
from parcae.dsl.math import Z29Expr
from parcae.dsl.primitives import define_primitive
from parcae.dsl.theory import Theory, Param
@define_primitive(
name="poly2_mod29",
signature="(i: Z29, c2: Z29, c1: Z29, c0: Z29) -> Z29",
)
def poly2_mod29(i: Z29Expr, c2: Z29Expr, c1: Z29Expr, c0: Z29Expr) -> Z29Expr:
return (c2 * i * i) + (c1 * i) + c0
@Theory(
name="quadratic_polynomial_stream",
family="keyed_stream",
tier="B",
interrupts="none_by_design",
)
class QuadraticPolynomialStream:
c2: Param[int] = Param(min=0, max=28)
c1: Param[int] = Param(min=0, max=28)
c0: Param[int] = Param(min=0, max=28)
def structural_claim(self) -> str:
return (
"Speculative. Quadratic keystream on Z29; exhaustive verify proves "
"totality/determinism only -- not LP statistics (Tier B)."
)
def keystream_at(self, i: Z29Expr) -> Z29Expr:
return poly2_mod29(i, self.c2, self.c1, self.c0)
def encrypt_step(self, x: Z29Expr, i: Z29Expr) -> Z29Expr:
return x + self.keystream_at(i)
def decrypt_step(self, x: Z29Expr, i: Z29Expr) -> Z29Expr:
return x - self.keystream_at(i)Save a file under theories/ (or use the repo copies) and compile:
# Compose (Atbash o Caesar) -- same idea as Liber Primus "Koan 1" style
$BIN/parcae-compile --data-dir data theories/examples/full_lifecycle_example.py
$BIN/parcae-validate --data-dir data --theory koan1_style@1
# New-math keystream
$BIN/parcae-compile --data-dir data theories/examples/new_math_example.py
$BIN/parcae-validate --data-dir data --theory quadratic_polynomial_stream@1Full sources: theories/examples/. Language rules:
docs/spec/dsl.md (whitelist imports, tiers, HotLoop vs
OuterControl). Stubs vs compiler:
docs/architecture/dsl-stubs.md.
Stuck on CLIs? docs/spec/tools.md. Alphabet background
(optional): docs/research/gematria-primus.md.
Liber Primus is a rune ciphertext. Early pages are solved with known methods (Atbash, Vigenère + skips, totient streams, ...). The rest is still open.
Parcae's job is not vibes. It is to:
- Encode the frozen Gematria Primus alphabet Z29.
- Reproduce solved pages as regression fixtures so the code cannot drift.
- Give you fast, deterministic tools to search, score, and record hypotheses for the unsolved material -- including GPU batches and an optional agent.
| Number | Meaning |
|---|---|
| 29 | Alphabet size. Every rune -> index 0...28 (Index29). All crypto math is mod 29. |
LP2 0-55 |
Still-unsolved Liber Primus image range this toolkit aims to push forward. |
| CMake ≥ 3.25 / Python ≥ 3.11 | Build / stubs+agent requirements. |
Solved-page oracles: data/fixtures/solved/. Research:
docs/research/.
ciphertext / runes
│
▼
Index29 stream ← 29-symbol Gematria Primus
│
├── tokenize / decode / score / validate (C++ CLIs)
├── generate -> rank -> hypothesis (candidate search)
├── CUDA twins (optional, bit-parity with CPU)
└── theory.py ──► parcae-compile ──► artifact (verified theory)
▲
Python stubs = IDE only
Rule of thumb: if it changes ciphertext -> plaintext math, it lives in C++ (and optionally CUDA). Python is for authoring hints and the optional agent loop.
| Goal | Start here |
|---|---|
| Learn the 29-rune alphabet | docs/research/gematria-primus.md |
| Reproduce a solved page | parcae-decode --manifest ... + parcae-validate |
| Apply Hill / autokey / grid transforms | parcae-decode --transform-id … (transforms.md catalog) |
| Score a candidate plaintext | parcae-score |
| Enumerate small transform grids | parcae-generate -> parcae-rank |
| Record a research hypothesis | parcae-hypothesis + docs/spec/hypothesis-workspace.md |
| Author a new theory (DSL) | Theory DSL examples; theories/examples/ |
| Drive tools from an LLM | agents/ (parcae-agent) |
| Closed-loop search (workspace cycles) | search-handbook.md (parcae-search-cycle; --allow-extended-families / --allow-theory-uri) |
| GPU fused search / throughput | docs/architecture/cuda-build.md |
There are two Python packages. Neither replaces the C++ crypto core.
| Purpose | Autocomplete / type stubs while writing theory.py |
| Install | cd python && pip install -e ".[dev]" |
| Compiler? | No. Calling ops / apply / test runners raises ParcaeDslStubError |
| Real compile | parcae-compile path/to/theory.py (C++ pipeline) |
| Syntax-only dump | python -m parcae.dsl.ast_dump theory.py -> AST JSON (still not verification) |
theory.py --import--> parcae.dsl stubs (IDE only; calls raise)
│
├── ast_dump -----> dsl_ast_json.v0 (syntax wire format)
│
└── parcae-compile -> data/theories/... (only verified path)
| Doc | Role |
|---|---|
docs/architecture/dsl-stubs.md |
Stubs vs compiler (read first) |
docs/architecture/python-transpiler.md |
Full compiler architecture |
docs/spec/dsl.md |
Language rules |
theories/examples/README.md |
Example theories |
Compile an example (after building tools):
$BIN/parcae-compile --data-dir data theories/examples/full_lifecycle_example.py
$BIN/parcae-validate --data-dir data --theory koan1_style@1Artifacts land under data/theories/<name>/<version>/ with URI form
parcae://theories/<name>@<version>.
| Purpose | LLM chooses allow-listed C++ tools; never reimplements Z29 |
| Install | cd agents && pip install -e ".[dev]" |
| Crypto? | No. Subprocess -> parcae-* --json only |
| Handbook | docs/architecture/agent-handbook.md |
cd agents
python -m parcae_agent doctor --config configs/ollama.example.yaml
python -m parcae_agent run --config configs/ollama.example.yaml \
--prompt "List catalog transforms for a-warning"Normative contracts: docs/spec/tools.md. Agent allow/deny:
docs/spec/agent-tools.md.
| CLI | Role |
|---|---|
parcae-tokenize |
Ciphertext -> tokens / indices |
parcae-decode |
Apply a catalog transform (or fixture manifest) |
parcae-score |
Deterministic scores (ic_mod29, chi2_english_gp_v0, ...) |
parcae-validate |
Fixture oracles or theory artifacts |
parcae-catalog |
List transforms, scores, generators, theories |
parcae-generate |
Expand bounded candidate grids |
parcae-rank |
Top-k score candidates |
parcae-hypothesis |
Workspace hypothesis CRUD / score / ensure --layout research |
parcae-compile |
Theory DSL -> verified artifact |
parcae-sweep |
Expand theory param_grid plans |
parcae-search-cycle |
Closed-loop search (job → batch → hypotheses; --allow-extended-families / --allow-theory-uri) |
parcae-search-run |
Fused search / throughput-style sweeps |
parcae-parity / parcae-parity-gen |
CPU↔CUDA parity records |
parcae-blind-crack |
Research battery on locked fixtures |
parcae-bench |
Benchmark & diagnostics (slo / accuracy / hardware / probe / all; deny-listed for agents) |
parcae-throughput-tiers |
CUDA SLO compat ≡ parcae-bench --suite slo --extended --allow-cuda (deny-listed) |
Manifest mode:
$BIN/parcae-decode --data-dir data \
--manifest data/fixtures/solved/a-warningFlag mode (Atbash):
$BIN/parcae-decode --data-dir data \
--input data/fixtures/solved/a-warning/ciphertext.txt \
--transform-id atbash --direction decryptKeyed solved page (Welcome / DIVINITY + skips):
$BIN/parcae-decode --data-dir data \
--manifest data/fixtures/solved/welcomeScore helpers:
$BIN/parcae-score --data-dir data --list
$BIN/parcae-score --data-dir data --score-id ic_mod29 --indices \
--input data/fixtures/cli/score-indices.txtStart at the top of each column; go deeper as needed.
| If you want... | Read |
|---|---|
| Research / alphabet / solved methods | docs/research/ |
| Binding specs (MUST/SHOULD) | docs/spec/ |
| How the C++ / CUDA / DSL stack fits | docs/architecture/ |
| Theory DSL compiler | docs/architecture/python-transpiler.md |
| DSL stubs vs compile | docs/architecture/dsl-stubs.md |
| CMD agent operator guide | docs/architecture/agent-handbook.md |
| Local CUDA build notes | docs/architecture/cuda-build.md |
| Example theories | theories/examples/ |
| CUDA sources (VS) | Parcae/Parcae/cuda/ |
Architecture hub: docs/architecture/README.md.
research + specs -> docs/research, docs/spec done
CPU reference -> include/, fixtures, scores, CLIs done
CUDA parity -> Parcae/Parcae/cuda/ twins done -- v0.3.0-cuda-parity
Theory DSL compiler -> theories/ + include/parcae/dsl/ done -- v0.5.0-theory-dsl
CMD agent tooling -> agents/ + agent-facing CLIs in progress -- v0.6.0-agent-tools
Search engine loop -> GPU ↔ candidates ↔ hypotheses done -- v0.7.0-search-engine
Smart DSL + console -> scopes/Select/#ignore + dashboard done -- v0.8.0-dsl-console
Bench & diagnostics -> parcae-bench SLO/accuracy/hw/probe done -- v0.9.0-bench
Catalog + DSL math -> Hill/autokey/grids + z29_matmul/det/autokey_shift done -- v1.0.0
CUDA catalog + Pack D -> 1.0 GPU twins + hill/autokey export + dsl_spec 1.1 next -- v1.1.0
Frozen CUDA commit list: docs/architecture/cuda-roadmap.md.
CMD-agent plan: docs/architecture/agent-tooling.md.
Search engine: docs/architecture/search-engine.md
(exit checklist engineering-green) |
commit list docs/architecture/search-roadmap.md |
operator guide docs/architecture/search-handbook.md |
spec docs/spec/search-loop.md.
Smart DSL + console exit: docs/architecture/dsl-console-exit.md
(v0.8.0-dsl-console) | compiler guide
docs/architecture/python-transpiler.md |
console progress in docs/architecture/search-handbook.md.
Bench exit: docs/architecture/bench-exit.md
(v0.9.0-bench) | operator guide
docs/architecture/bench-diagnostics.md.
1.1.0 exit freeze (CUDA catalog parity + Pack D):
docs/architecture/cuda-catalog-parity.md
(v1.1.0) | Pack D checklist
docs/architecture/dsl-pack-d.md.
- Advance unsolved LP2
0-55with systematic search and scored hypotheses. - Ground truth from solved pages -- fixtures must keep reproducing known decrypts.
- CPU reference ↔ CUDA parity -- same
Index29math, comparable scores under documented FP rules. - Deterministic agent tools -- LLMs call CLIs; they do not own the crypto.
- C++-first core -- Python is authoring DX + optional agent, not a second math engine.
- Embedding original puzzle JPGs in the repo
- Publishing "solutions" without reproducible fixture-grade evidence
- Putting an LLM inside transform / score kernels
- Requiring a GPU for everyday CPU development or hosted CI
Day-to-day GPU work: open Parcae/Parcae.slnx, build x64
with the CUDA Toolkit VS integration. Sources:
Parcae/Parcae/cuda/.
cmake -S . -B build-cuda -DPARCAE_BUILD_CUDA=ON -DPARCAE_BUILD_TESTS=ON -DPARCAE_BUILD_TOOLS=ONFull notes (flags, Catch2 tags, skip behavior when Toolkit is absent):
docs/architecture/cuda-build.md.
Hosted CI (.github/workflows/ci.yml) stays CPU-default (PARCAE_BUILD_CUDA=OFF).
It also gates smart DSL ([dsl-smart]), console progress ([cli-progress]),
deterministic bench ([bench] -- spec/probe/accuracy/report; no absolute runes/s),
theory examples ([dsl][examples]), stale-spec rejection ([dsl][registry][stale]),
stub pytest, and scripts/check-dsl-examples.sh.
- C++:
.clang-format(LLVM-ish, 4-space), light.clang-tidy. - Prefer top-level classes and
#ifndefheaders; the project avoids C++ namespaces in library code. - Specs use RFC-style MUST / SHOULD / MAY (
docs/spec/README.md).
Issues and PRs welcome once you can show a green ctest (and, for DSL changes,
[dsl][examples] / stub tests as relevant).