Building the KERI Libraries Against the Singular Package
A contributor preparing the KERI integration with the redesigned Singular registry runs one command and learns whether the reusable KERI Aiken libraries still compile and test next to the published Singular registry package. When Singular's interface moves underneath them, the same command fails and names what moved: a value that is no longer exported, a module that no longer exists, or an approval that no longer commits to a field KERI relies on.
just check-singular-aiken
The command builds the Nix check in
aiken-singular-consumer/
from a clean checkout. Hosted CI runs the same check in the required
Onchain (Aiken) job and keeps the compiler, lock, test and control
receipts as the singular-aiken-consumer-<commit> workflow artifact for
fourteen days.
This is technical preparation. It shows that the libraries meet at the package interface. It does not show that KERI registration and conviction behave as the KERI model says once they run through Singular; that is the application work of #324.
Not for mainnet
The consumer package and the contracts it compiles are not for mainnet.
What the check assembles
flowchart TB
subgraph own["Consumer library"]
tests["Compatibility tests<br/>(committed)"]
keri["KERI onchain/lib<br/>(this commit)"]
end
subgraph deps["Dependencies"]
singular["singular/registry<br/>(public commit d69ab005)"]
prelude["Aiken prelude<br/>packages"]
end
own -->|compiled as the root| consumer["Consumer package<br/>Aiken v1.1.23"]
deps -->|compiled as packages| consumer
consumer -->|aiken check| results["Test results"]
results -->|compared with declared tests| agreement["Identity agreement"]
agreement -->|written with the controls| receipts["Receipts"]
The consumer package is assembled inside the Nix sandbox from sources only.
Its own library is the committed compatibility tests together with the KERI
libraries of the same commit. Its dependencies are the Singular registry
package at the public Singular commit
d69ab005,
the same commit the KERI Lean model is built against, and the shared Aiken
prelude packages at the versions both projects already
use. The compiler is the Aiken release every other onchain recipe of this
repository uses. The whole graph is compiled, then the compatibility tests and
the retained KERI registration tests are run.
The check then reads the tests declared in those modules and requires the tests the compiler reports to be exactly the same set, every one passed, and none of the sets empty. A selection that silently runs fewer tests fails the check.
The package boundary
flowchart LR
subgraph keri["KERI"]
datum["Checkpoint datum"]
aid["Identifier key"]
holding["Checkpoint output"]
end
subgraph singular["Singular registry package"]
approval["Registration approval"]
active["Active token name"]
guards["Fold holding guards"]
end
datum -->|hashed as the initial datum| approval
aid -->|registry key| approval
aid -->|registry key| active
holding -->|address and deposit| approval
holding -->|checked for one active token| guards
A package that depends on Singular sees only its library modules; its
validators are not importable. The KERI side uses the redesigned
singular_state namespaces:
| Singular value | What KERI gives it | What the compatibility tests require |
|---|---|---|
Registration approval (approval_for, registration_preimage, approval_name) |
The checkpoint output: its inline checkpoint datum, its address and its deposit, and the identifier key | The approval Singular recomputes equals the hash of the published approval structure, and changes when any one of the registry, the key, the stake part of the address, the payment part of the address, the initial checkpoint datum or the deposit changes |
Approval matching (change_approval) |
A request carrying the approval | The approval of one change is not accepted for another |
Active token name (active_token_name) |
The registry and the identifier key | One name per registry and key, never equal to a state, configuration, termination or registration approval name of the same key |
Registry identity (registry_identity) |
The registry's seed and state policy | The registry is named by the state token of its seed |
Holding guards (registration_placement, registration_deposit) |
A checkpoint output holding the active token | One active token in the checkpoint output is accepted and a second one elsewhere is refused; the registration deposit floor is held and one lovelace less is refused |
Singular calls nothing in KERI. The only KERI value it reads is the checkpoint output named by a registration: its address, its value and the datum it carries, which Singular hashes without interpreting. These requirements are the ones the public Singular issues #742 and #743 state for a registration and its approval.
What the tests show, and what they do not
| Test set | What it is | What it shows |
|---|---|---|
| Fifteen compatibility tests | New tests importing KERI and Singular together, over the generated KERI registration vectors | The published interface accepts real KERI values and commits to every field of the registration approval |
| Sixty-four retained registration tests | The existing KERI registration tests, run unchanged inside the consumer | The legacy KERI registration predicate still compiles and holds next to the Singular package |
Reusing the legacy registration predicate is not the redesigned application. The redesign moves registration to a KERI approval policy that Singular's fold consumes; that policy does not exist yet, so no test here can show that KERI and Singular agree on a registration as a whole.
The 12 October 2026 local probe passed two compatibility tests and the same sixty-four registration tests. Those two probe tests were never committed and are retired; the fifteen compatibility tests replace them. The registration tests, the predicate they exercise and their generated vectors are unchanged since that probe, and so are the sixty-four test names.
Controls
Each control runs inside the same check and must fail for its stated reason, or the check fails.
| Control | What changes | Required failure |
|---|---|---|
| Skipped test | One test is dropped from the reported results | The discovered and executed test sets differ |
| Broken interface | The Singular package stops exporting approval_for |
Compilation fails with an unknown import of approval_for |
| Broken import | The compatibility tests import a Singular module that does not exist | Compilation fails with an unknown module |
| Changed commitment, one per approval field | The Singular approval stops committing the registry, the key, the stake part, the payment part, the initial datum or the deposit | The compatibility test for that field fails, and no retained registration test changes verdict |
| Invalid lock | The consumer lock names another package version | The check stops before compiling |
Decisions
| Decision | Chosen | Rejected, and why |
|---|---|---|
| Where the consumer lives | A separate package, aiken-singular-consumer/, assembled from sources at check time |
Adding Singular to onchain/: both projects have top-level modules named types and lib, so the names collide |
| How the KERI libraries enter | As the consumer's own library | As a dependency: Aiken runs only the tests of the project being checked, so the retained registration tests would never run |
| Which Singular | The public commit the Lean model is built against | A moving main or a local checkout: neither is reproducible from a clean clone |
| Which compiler | The Aiken release of every other onchain recipe here | Singular's declared compiler: the libraries must keep compiling with the compiler KERI ships its scripts with |
| Which tests define success | The declared tests of the selected modules, compared with the reported ones | A pass count: a selection that runs nothing reports zero failures |
Remaining production work
The check stops at the package boundary. The production work it leaves, tracked in #324:
- the KERI approval policy that checks the inception evidence, signatures, receipts, refund binding, bond amounts and initial checkpoint datum at booking, and mints the registration approval Singular consumes (#348);
- a token policy for checkpoint and fact tokens separate from the approval policy, since a registration request carries exactly one application token;
- the registry identity and active token in the checkpoint datum, which changes the checkpoint datum encoding;
- the encoding of the approval preimage in the off-chain builders, bound to the published Singular byte specification once it is settled upstream;
- termination approvals for conviction (#350), whose upstream vectors are not settled (#743), and the released Singular compatibility of #747.