kryphocron 0.1.0

Privacy-first ATProto substrate primitives: type architecture, audit vocabulary, inter-service auth, and encryption hook surfaces
# 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.

[![License: MPL-2.0](https://img.shields.io/badge/license-MPL--2.0-brightgreen.svg)](https://www.mozilla.org/MPL/2.0/)
[![Crates.io](https://img.shields.io/crates/v/kryphocron)](https://crates.io/crates/kryphocron)

> **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]#known-limitations-in-v01); 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
  `AuditEncryptionResolver` and `RecordEncryptionResolver`
  trait 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 `DidResolver` and encryption resolvers receive
  key material from operator code.
- ATProto record validation beyond what `kryphocron-lexicons`
  commits.

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

```toml
[dependencies]
kryphocron = "0.1"
```

A minimal substrate-side integration sketch:

```rust,ignore
use kryphocron::{
    AuditSinks, NoEncryption, OracleSet, TraceId,
    authority::{NoInspectionNotifications, issue_user, v1::ViewPrivate},
    verification::verify_jwt,
};

// 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 = TraceId::from_bytes(/* fresh per-request */);
let verified = verify_jwt(
    authorization_header,
    &local_audience,
    &did_resolver,
    &jwt_config,
    deadline,
    trace_id,
).await?;
let ctx = kryphocron::ingress::from_xrpc_request(
    verified, trace_id, sinks, oracles,
);

// 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 = issue_user::<ViewPrivate>(&ctx, target_resource_id)?;
let bound = proof.bind(&ctx, &target_resource_id).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:

1. **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.

2. **Capabilities are unforgeable in safe code.** Code outside
   the crate's `authority` module cannot construct authorization
   proofs. Sealed traits and a private token type carried in
   `PhantomData` on every proof type enforce this.

3. **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.

4. **Audit reflects action, not intent.** Audit events fire on
   the *binding* of a capability proof (success or failure), not
   on its issuance.

5. **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., `NoEncryption` for 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`, denies
  `todo!()` / `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), `AuthContext` with attribution-chain rehydration from
  upstream delegation.
- **§4.1 tier-aware visibility**`tier::visible_to(tier, ctx)`
  predicate.
- **§4.2 scope-narrowing derivation**`AuthContext::derive_for`
  with three legal narrowings (drop-to-anonymous,
  narrow-capabilities, service-to-service); audit emits a
  `DerivedContext` event on every attempt (success and failure).
- **§4.3 capability issuance**`issue_user`, `issue_channel`,
  `issue_substrate`, `issue_moderation` chokepoints 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>` via `tokio::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_unwind` panic catchment.
- **§5 lexicon strategy** — closed-namespace registry via the
  companion `kryphocron-lexicons` crate; `Tier::from_nsid`
  consults the build-time-authoritative
  `KRYPHOCRON_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), `CapabilityClaim` wire
  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`,
  `RecordEncryptionResolver` trait shapes; v1 default is
  `NoEncryption` (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 ServiceIdentity` and `session_id` on
  `ChannelBound` / `ChannelReborrowFailed`.
- Substrate-class `scope_repr` on `ScopeBound`.
- Moderation-class `case ModerationCaseId` on `ModeratorInspected`
  / `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_to` is 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]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/kryphocron` repo. 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`](https://github.com/skydeval/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.