kryphocron
Privacy-first ATProto substrate primitives. Type architecture, audit vocabulary, inter-service auth, and encryption hooks for substrates that need to commit threat-model invariants structurally rather than as policy.
Early access — v0.1.x is the published surface of an in-progress substrate. The wire-format and audit-event vocabularies are committed (no breaking changes within 0.1.x); the consumer-facing API surface (trait shapes, constructor signatures, accessor ergonomics) may evolve through the 0.x cycle as downstream integrations surface concrete needs. v0.2 lands sealed per-class extraction traits that flip a few placeholder audit-payload fields to real values (see Known limitations in v0.1); v0.5+ work on the companion Aurora-Locus federation surface may motivate additional 0.x evolution. Pin tilde (
~0.1) or full minor (0.1) per your tolerance.
What it is
kryphocron is the foundational primitives crate for the
Kryphocron substrate — an ATProto-compatible identity and
publishing substrate built around the discipline that the
wrong path is harder to write than the right path. Misuse of
a primitive should be a compile error wherever possible, a
runtime error otherwise, and a silent success never.
The crate ships:
- Tier-aware envelope types (
Tier,Tiered<T>,HasNsid) that make Public / Private classification a structural property of the type system. A function emitting to a public surface cannot, by type, accept a private-tier value. - Capability proofs — sealed, unforgeable in safe Rust via
PhantomData<sealed::Token>markers. AuthContext— the in-process authentication context type with attribution-chain rehydration from upstream delegation.- Audit pipeline — composite-rollback machinery, fallback- sink contract, and a 30+ variant audit-event vocabulary covering user, channel, substrate, and moderation classes.
- Inter-service auth — JWT verification, capability claim wire format with W11/W12/W13 monotonicity invariants, trust declarations, DID resolution, and the three-message sync handshake protocol.
- Encryption hook surfaces — operator-pluggable
AuditEncryptionResolverandRecordEncryptionResolvertrait shapes; v1 ships the surfaces, v2+ ships the algorithm variants.
What it is NOT
kryphocron deliberately does NOT ship:
- Concrete encryption implementations. The trait surfaces are committed; specific ciphers are operator-supplied.
- A PDS, AppView, Graph, or any other operator substrate component. Those are downstream crates that build on kryphocron primitives.
- HTTP transport, TLS, or wire I/O. The crate produces and consumes byte arrays; operators wire up their own transports.
- Key management, KMS integrations, or rotation orchestration.
The crate's
DidResolverand encryption resolvers receive key material from operator code. - ATProto record validation beyond what
kryphocron-lexiconscommits.
The discipline is door-open, not door-ajar: where an implementation choice is operator policy, the crate commits a trait shape and leaves the implementation to operators.
Quick start
[]
= "0.1"
A minimal substrate-side integration sketch:
use ;
// At substrate startup, install audit sinks, oracles, the DID
// resolver, the deployment correlation key, the inspection-
// notification queue, and (optionally) an encryption resolver set.
let resolver_set = NoEncryption; // v1 default; no encryption.
let inspection_queue = NoInspectionNotifications; // no-op default.
// Per-request: verify a JWT, build an AuthContext.
let trace_id = from_bytes;
let verified = verify_jwt.await?;
let ctx = from_xrpc_request;
// Issue a capability proof against the requester's context, then
// bind it against the target. `bind` and `reborrow` are async —
// the §4.3 pipeline runs inside `composite_audit` which is
// async, and timing-channel equalization sleeps via tokio.
let proof = ?;
let bound = proof.bind.await?;
// `bound` grants access to the subject; the audit pipeline has
// already fired the terminal CapabilityBound event structurally.
let subject = bound.subject;
The substrate's value isn't in the API ergonomics — it's in the threat-model invariants the type system enforces.
Bind API asymmetry
Three of the four *Proof::bind methods take (self, ctx, target).
ModerationProof::bind takes a fourth argument:
rationale: ModeratorRationale (a length-bounded operator-declared
string, mandatory for every moderation action per §6.5). Operators
writing generic bind-dispatch code need to handle this asymmetry —
the rationale is bind-time input, not issuance-time, matching
operator workflows where the moderator commits a rationale at
the moment of action.
Design discipline
Five organizing principles:
-
The wrong path is harder to write than the right path. Misuse of a primitive is a compile error wherever possible, a runtime error otherwise, and a silent success never.
-
Capabilities are unforgeable in safe code. Code outside the crate's
authoritymodule cannot construct authorization proofs. Sealed traits and a private token type carried inPhantomDataon every proof type enforce this. -
Tier is not a label, it's a structural property. A function that emits to a public surface cannot accept a private-tier value, by type, not by runtime check.
-
Audit reflects action, not intent. Audit events fire on the binding of a capability proof (success or failure), not on its issuance.
-
Door-open, not door-ajar. Where the spec defers a choice to operator policy, the crate commits the trait surface and leaves the implementation pluggable. Defaults are explicit (e.g.,
NoEncryptionfor the encryption resolver set), not implicit.
Status
v0.1.0 publishes the kryphocron substrate to three audiences:
- ATProto-adjacent operators evaluating whether the substrate's design discipline fits their threat model. The §4 type architecture and §6 audit vocabulary are committed; the trait surfaces are stable enough to inform a "would we build on this?" decision.
- Downstream integrators (Aurora-Locus federation surface work, future PDS / AppView / Graph substrates) consuming kryphocron as a dependency. The wire-format and audit-event shapes are committed within 0.1.x; consumer-facing API ergonomics may evolve as integrations surface concrete needs.
- Adversarial reviewers stress-testing the substrate's
threat-model commitments. The crate forbids
unsafe, deniestodo!()/unimplemented!(), and ships with 340+ tests pinning behavior across user, channel, substrate, and moderation classes plus the §4.6 timing-channel equalization surface.
v0.1.0 ships the kryphocron substrate's authority discipline end-to-end:
- §4 type architecture — tier-aware envelopes, sealed
capability proofs (no struct-literal construction outside the
crate),
AuthContextwith attribution-chain rehydration from upstream delegation. - §4.1 tier-aware visibility —
tier::visible_to(tier, ctx)predicate. - §4.2 scope-narrowing derivation —
AuthContext::derive_forwith three legal narrowings (drop-to-anonymous, narrow-capabilities, service-to-service); audit emits aDerivedContextevent on every attempt (success and failure). - §4.3 capability issuance —
issue_user,issue_channel,issue_substrate,issue_moderationchokepoints with per-class requester-authority enforcement. - §4.3 bind + reborrow — async pipelines across all four
capability classes: pre-checks → stage 0 deprecation gate
(write-semantics user-class + moderation) → stage 2 oracle
consultation (user-class) → stage 5 predicate (user-class) →
audit emit via
composite_audit→ stage 6 timing equalization (user-class) → return. - §4.6 timing-channel equalization —
equalize_timing+equalize_timing_target_for::<C>viatokio::time::sleep. Coarse equalization to a deployment-configured target; NOT a constant-time discipline. Granularity is limited by the host scheduler (ms-scale jitter on Windows/WSL; tighter on Linux), and a network-side adversary with high-resolution latency measurement can still see through the equalization for short-running operations. Operators with strong timing-channel threat models should treat this as a coarse first defense and layer constant-time-discipline cryptographic primitives + a hardened timing primitive (e.g. randomized jitter, ring-buffered release queues) at the perimeter. Full hardening is v2+ work. - §4.9 composite-audit machinery — class-priority commit
order (substrate → moderation → user → channel), rollback
fan-out to already-committed sinks, fallback-sink escalation
with
catch_unwindpanic catchment. - §5 lexicon strategy — closed-namespace registry via the
companion
kryphocron-lexiconscrate;Tier::from_nsidconsults the build-time-authoritativeKRYPHOCRON_LEXICON_REGISTRY. - §6 audit event vocabulary — 30+ variants across user,
channel, substrate, moderation classes; encrypted-layer split
via
TargetRepresentation::structural_only/TargetRepresentation::with_sensitive. - §6.7 inspection-notification queue — moderation-class fan- out alongside the composite-audit emission; outside rollback semantics per the "notifications are diagnostic, not authoritative" discipline.
- §7 inter-service auth — JWT verification (Ed25519 default;
ECDSA recognized but not v1-shipped),
CapabilityClaimwire format with W11/W12/W13 monotonicity invariants, trust declarations, DID resolution + rotation evidence, three-message sync handshake protocol, delegation receipts + chain rehydration. - §8 encryption hook surfaces —
AuditEncryptionResolver,RecordEncryptionResolvertrait shapes; v1 default isNoEncryption(no-op resolver set), operator plug-ins fill in real algorithm support.
Known limitations in v0.1
None of these are security bugs. They're places where the
v0.1 surface is narrower than the spec's full v1 commitment, with
the gap closed in v0.2+ enrichment passes. Each item ships with a
programmatic signal where operators benefit from one
(PayloadCompleteness::PartialV01 on the audit-event variants
below), and a public README disclosure where they don't.
A few audit-event payload fields ship with placeholder data in v0.1 pending sealed per-class extraction traits in v0.2:
- Channel-class
peer ServiceIdentityandsession_idonChannelBound/ChannelReborrowFailed. - Substrate-class
scope_repronScopeBound. - Moderation-class
case ModerationCaseIdonModeratorInspected/ModeratorTookDown/ModeratorRestored.
The affected variants carry a payload_completeness: PayloadCompleteness field. v0.1 sets this to
PartialV01; v0.2 flips it to Full when the sealed extraction
traits land. Operators consuming the audit stream branch on this
discriminator to gate dashboards or alerting that would otherwise
silently render placeholder data as real values.
Other v0.2 enrichments:
- User-class oracle consultations consult only the universal block-vs-resource-owner query in v0.1 (multi-query consultations land alongside a per-capability oracle-results-builder in v0.2).
tier::visible_tois tier-only in v0.1; an audience-aware overload lands in v0.2.- Moderation-class reborrow miss is silent at the audit layer in v0.1 (no fitting variant in v1's audit vocabulary); v0.2 adds the variant.
Wire-format-touching changes are reserved for a future v0.2 or v1.0 cycle. v0.1.x patches are non-breaking only.
Closed-namespace lexicon registry
kryphocron's tier classification is closed-namespace: only NSIDs
in the tools.kryphocron.* namespace are known to the substrate's
registry. Tier::from_nsid consults
KRYPHOCRON_LEXICON_REGISTRY (compiled in from the companion
kryphocron-lexicons crate) and returns
Err(UnknownNsid::NotRegistered) for NSIDs outside that
namespace — including ATProto-ecosystem NSIDs like
app.bsky.feed.post or com.atproto.repo.strongRef. There is
no default-to-Tier::Public path; unknown NSIDs are a hard
error, not a permissive fallback.
Operators running kryphocron alongside other ATProto lexicon
surfaces — for example, a PDS that handles both app.bsky.*
records and tools.kryphocron.* records — must be aware that
kryphocron's tier discipline applies only to its own namespace.
Non-kryphocron records are not classified by the kryphocron tier
system; the consuming substrate is responsible for handling
them per its own discipline (typically a separate
classification layer, or a routing decision that hands
non-kryphocron records off before the kryphocron tier check).
This is a deliberate v0.1 design choice. Cross-namespace
classification (treating app.bsky.* records as Tier::Public
by default, sourcing tier from app.bsky.* lexicons' own
metadata, etc.) is reserved for a future release if downstream
operators surface concrete demand. The current discipline
foreclose ambiguity: a NSID either is, or isn't, in the
substrate's trust boundary, with no silent middle ground.
Feedback and contributing
Three channels, sorted by what you have:
- Integration pain or substrate-side bug → open a GitHub issue at https://github.com/skydeval/kryphocron/issues. Bug reports against the §4 / §6 / §7 surfaces should include a minimal reproducer (a failing test, a code excerpt) so the issue lands as a concrete fix.
- Security issue → see SECURITY.md. Please don't open a public issue for vulnerabilities; the security policy describes the disclosure path.
- Design feedback, threat-model questions, or open questions
about v0.2+ direction → GitHub Discussions on the
skydeval/kryphocronrepo. The substrate's design discipline is exploratory; questions about why a particular shape shipped (or didn't) are welcome and inform the v0.2 enrichment passes.
The companion lexicon JSON lives in
kryphocron-lexicons;
suggestions on the lexicon vocabulary itself belong there, not
here.
License
MPL-2.0 (Mozilla Public License 2.0). See LICENSE.
The MPL is file-level copyleft: modifications to MPL-2.0-licensed
source files must be released under MPL-2.0; downstream crates
linking to kryphocron are unconstrained and may ship under
permissive licenses (MIT OR Apache-2.0 recommended for operator
substrate components like PDS, AppView, etc.).
The companion kryphocron-lexicons crate ships its lexicon JSON
files under CC0-1.0 (public domain dedication); the wire
vocabulary is universal and unencumbered.
Project shape
Kryphocron is a privacy-first ATProto substrate by @skydeval. The crate is MPL-2.0; the companion lexicon JSON is CC0-1.0. See LICENSE for the full text.
Related
kryphocron-lexicons— companion crate shipping the v1 lexicon JSON + Rust codegen wrappers. Required for consumers using lexicon-validated record types.