import { R as RelayWriteStore, a as RelayIdentity, b as RelayReadStore, I as IndexReadStore, c as IndexWriteStore, d as RelayOptions, S as StoredIdentityChain, e as SigningStore, f as RelayWriterState, C as CommitBatch, g as CommitResult, h as IndexRowBatch, i as IndexCursor, j as StoredOperation, k as StoredContentChain, B as BlobKey, l as StoredRevocation, m as StoredPublicCredential, L as LogEntry, n as StoredSignRequest, o as IndexOrderedCursor, p as IndexOrder, q as IndexIdentityRow, r as IndexContentRow, s as IndexCreditCursor, t as IndexCreditRow, u as IndexRecencyOrder, v as IndexArtifactRow, w as IndexCountersignatureQueryRow, x as IndexCredentialQueryRow, O as OperationKind, y as IndexOperationRow, z as RelayStats, A as OpOrigin, P as PendingOp, D as IngestionResult, E as SequenceResult } from './peer-client-Dh38bOpr.js'; export { F as AdmissionPolicy, G as AuthenticatedPrincipal, H as BlobCommit, J as CredentialRevocationStatus, K as DEFAULT_PROOF_SKEW_SECONDS, M as DEFAULT_PROOF_WINDOW_SECONDS, N as GossipProofSigner, Q as INDEX_BASE_PATH, T as INGESTION_MODES, U as IdentityProofOutcome, V as IndexCountersignatureRow, W as IndexCredentialRow, X as IndexProfile, Y as IndexSweepState, Z as IngestionMode, _ as IssuerRevocationEntry, $ as IssuerRevocationList, a0 as JtiReplayCache, a1 as OperationCommit, a2 as PeerClient, a3 as PeerConfig, a4 as PeerLogEntry, a5 as REVOCATIONS_BASE_PATH, a6 as RelayOpenApiOption, a7 as RelayPeerInfo, a8 as RelayStore, a9 as SigningDeclineResult, aa as SigningPutResult, ab as StoredCountersignature, ac as authenticateIdentityProof, ad as contentIdsFromCredential, ae as contentIndexRow, af as countersignatureIndexRow, ag as createCurrentStateProofResolver, ah as createHttpPeerClient, ai as createJtiReplayCache, aj as credentialRevocationStatus, ak as creditIndexRows, al as hasPublicStandingAuth, am as identityIndexRow, an as isIndexReadStore, ao as isIndexWriteStore, ap as isRelayWriteStore, aq as isRelayWriterState, ar as isSigningStore, as as isValidCredentialCid, at as issuerRevocationList, au as verifyContentAccess } from './peer-client-Dh38bOpr.js'; import { Hono } from 'hono'; import { VerifiedIdentity, VerifiedContentChain } from '@metalabel/dfos-protocol/chain'; import { ResolvedIdentity } from '@metalabel/dfos-protocol/credentials'; export { MAX_JTI_BYTES } from '@metalabel/dfos-protocol/credentials'; /** * Generate a relay identity and profile artifact, ingest both into the store * * Creates an Ed25519 keypair, signs an identity genesis operation, derives * the DID, then signs a profile artifact with the relay's name. Both the * identity genesis and profile artifact are ingested into the store so * they are available via the relay's proof plane routes. */ declare const bootstrapRelayIdentity: (store: RelayWriteStore) => Promise; /** * Bootstrap a relay identity from an EXISTING key + key ID, with optional pinned * timestamps and profile name. Used for deterministic bootstrap — e.g. the * dual-relay parity harness pins one key + one createdAt across both twins so * the relay's own genesis + profile log entries are byte-identical, and durable * relays that reload their key from storage. Mirrors the Go twin's * BootstrapRelayIdentityFromKey. */ declare const bootstrapRelayIdentityFromKey: (store: RelayWriteStore, params: { privateKey: Uint8Array; keyId: string; name?: string; createdAt?: string; }) => Promise; /** The store shape the projection worker needs. */ type IndexProjectionStore = RelayReadStore & IndexReadStore & IndexWriteStore; /** * Log entries projected, and content rows swept, per run. The cap that turns an * unbounded corpus sweep into work that drains across runs. */ declare const DEFAULT_INDEX_PROJECTION_BUDGET = 5000; interface IndexProjectionOptions { /** Combined per-run cap on swept content rows and projected log entries. */ budget?: number; } interface IndexProjectionRun { /** Log entries projected in this run. */ projected: number; /** Content rows recomputed by the resumable sweep in this run. */ swept: number; /** No sweep outstanding and the log cursor reached the tip. */ caughtUp: boolean; } /** * Advance the index projection by at most one budget's worth of work. * * Sweep first (outstanding fan-out is older than anything on the log tail), then * as many log entries as the remaining budget allows, then one `applyIndexRows` * and one `setIndexCursor`. * * ON FAILURE the cursor is NOT advanced and no rows are applied, so the same * work is retried on the next run. That is safe because every recompute is * convergent, and it is the honest failure mode: a projection that cannot make * progress stalls visibly at its cursor rather than skipping entries. */ declare const projectIndex: (store: IndexProjectionStore, options?: IndexProjectionOptions) => Promise; /** * Run the projection until it is caught up, or until `maxRuns` budgets are * spent. The reference relay calls this after an ingest batch; a deployment that * would rather not pay projection latency on the accepting path runs it from a * timer instead (`indexProjection: 'external'`). */ declare const drainIndexProjection: (store: IndexProjectionStore, options?: IndexProjectionOptions & { maxRuns?: number; }) => Promise; /** * Recompute the content rows that project a document, after its blob lands. * * A blob arrives on its own route, often after the operation that referenced it, * and it can turn a row's docSchema/title/profile projection from unknown to * known. Nothing on the operation log marks that moment, so this is the one * projection entry point the log does not drive. It is bounded by the reverse * lookup — the rows that name this documentCID and nothing else. */ declare const projectIndexAfterBlob: (documentCID: string, store: IndexProjectionStore) => Promise; interface CreatedRelay { /** Hono application implementing the DFOS web relay HTTP API */ app: Hono; /** The relay's DID */ did: string; /** Sync operations from all configured sync peers (call on a schedule) */ syncFromPeers: () => Promise; /** * Advance the `/index/v0` projection by one budget's worth of work. * * The relay drives this itself after each accepted batch unless * `indexProjection: 'external'` is configured, in which case this is the whole * of index maintenance and the operator calls it on a timer. A relay whose * store does not implement `IndexWriteStore` reports nothing to do. */ projectIndex: () => Promise; } /** * Split items into batches of at most `size`, preserving order with no loss. * gossip() uses this to stay within the receiver's per-batch cap; exported so * the split behavior is directly testable (mirrors Go's maxGossipBatch chunking, * whose TestGossipChunksLargeBatches drives the split directly). */ declare const chunkOps: (items: T[], size: number) => T[][]; /** * Create a DFOS web relay Hono application * * The returned app is portable — mount it on any Hono-compatible runtime * (Node.js, Cloudflare Workers, Deno, Bun, etc.). * * When `identity` is provided, the relay uses the given DID and profile. When * omitted, a JIT identity and profile artifact are generated at startup. */ declare const createRelay: (options: RelayOptions) => Promise; declare const isValidDfosDid: (did: string) => boolean; interface DidVerificationMethod { id: string; type: 'Multikey'; controller: string; publicKeyMultibase: string; } interface DidServiceEntry { id: string; type: string; serviceEndpoint?: unknown; [key: string]: unknown; } interface DidDocument { '@context': [string, string]; id: string; controller: string; verificationMethod: DidVerificationMethod[]; authentication?: string[]; assertionMethod?: string[]; capabilityInvocation?: string[]; service?: DidServiceEntry[]; } interface DidDocumentMetadata { created?: string; updated?: string; deactivated: boolean; operationCount: number; } interface DidResolutionResult { '@context': string; didDocument: DidDocument; didResolutionMetadata: { contentType: string; }; didDocumentMetadata: DidDocumentMetadata; } /** * Build a W3C DID Document from a verified identity's terminal state * (DID-METHOD.md §4). A deactivated identity resolves to a minimal document with * an empty verification-method set and no verification relationships (§5.4). */ declare const identityToDidDocument: (state: VerifiedIdentity) => DidDocument; /** * Build a DIF Universal Resolver resolution result from a resolved chain * (DID-METHOD.md §5.2.2). Pure — the chain is already verified terminal state. */ declare const resolveDidDocument: (chain: StoredIdentityChain) => DidResolutionResult; /** * In-memory relay store — all data lives in Maps, lost on restart. * * The reference implementation of EVERY contract: the reads, `commit`, both * sides of the index profile, the signing mailbox, and the relay's * writer-internal bookkeeping. Suitable for development, testing, and * short-lived relay instances. */ declare class MemoryRelayStore implements RelayReadStore, RelayWriteStore, IndexReadStore, IndexWriteStore, SigningStore, RelayWriterState { private signRequests; private operations; private identityChains; private contentChains; private blobs; private countersignatures; private operationLog; private peerCursors; /** Keyed by `issuerDID::credentialCID` for issuer-scoped revocation */ private revocations; /** Keyed by credential CID */ private publicCredentials; /** Identity projection rows keyed by DID. */ private indexIdentityRows; /** Content projection rows keyed by contentId. */ private indexContentRows; /** Public-head credit projection rows grouped by contentId. */ private indexCreditRows; /** Accepted content-operation signer sets keyed by contentId. */ private indexContentSigners; /** * HAS-EVER-PROVED key reverse index: multibase public key → the DIDs whose * chains ever PROVED it into a role. Rows accumulate (a rotation removes * nothing) and are never deleted (a deleted identity keeps its rows), which is * exactly what makes the `key=` filter answer "what has this key ever * controlled" rather than "what does it control now". * * Append-only is load-bearing twice over. It is what makes has-ever-proved * accumulate at all, and — because the maintenance writer hands over the * chain's monotonic `provedKeys` after every accepted op — it is why the * index converges on the same rows whether it was maintained incrementally or * rebuilt from scratch. A key nothing ever proved never enters, which is what * stops a stranger from burning a key by declaring it. */ private indexIdentityKeys; /** Countersignature projection rows keyed by cid (carry witnessDID column). */ private indexCountersignatureRows; /** Relay-observed operation-log rows keyed by operation CID. */ private indexOperationRows; /** * The multibase public key each accepted operation's signature verified * against at ingest, keyed by operation CID — the stored `signerKey` column of * the operation row, held beside the row rather than on it because the wire * row is metadata-only (no signer field is served). An op whose signer key did * not resolve has no entry, and therefore matches no `signerKey=` value. */ private indexOperationSignerKeys; /** Standalone artifact projection rows keyed by artifact cid. */ private indexArtifactRows; /** Where the index projection worker got to. */ private indexCursor; /** * Persist one accepted operation, or one document blob, whole. * * ATOMICITY IN A SINGLE-THREADED, IN-MEMORY STORE. There is no transaction to * open, so the property comes from two rules, and both are load-bearing: * * 1. NOTHING AWAITS between the first mutation and the last. Every mutator the * apply block calls is a SYNCHRONOUS private method for exactly this * reason — one `await` on a trivially-synchronous helper is still a * microtask yield, and a concurrent read route (which does not take the * chain-state lock) could observe the operation row before its log entry. * Do not make one of them async. * 2. EVERYTHING IS VALIDATED FIRST, against nothing but the arguments, so the * apply block has no way to fail partway and leave a half-commit behind. * * A durable store gets the same property from its transaction. */ commit(batch: CommitBatch): Promise; applyIndexRows(rows: IndexRowBatch): Promise; getIndexCursor(): Promise; setIndexCursor(cursor: IndexCursor): Promise; /** * Write state directly, bypassing `commit`. * * NOT PART OF ANY STORE CONTRACT and deliberately absent from the durable * shapes: this is the in-memory reference store, and a test that wants a * revocation already on file should say so in one line rather than construct a * whole operation to carry it. Nothing in the package calls it. */ seed(state: { operations?: StoredOperation[]; identityChains?: StoredIdentityChain[]; contentChains?: StoredContentChain[]; blobs?: { key: BlobKey; bytes: Uint8Array; }[]; revocations?: StoredRevocation[]; publicCredentials?: StoredPublicCredential[]; logEntries?: LogEntry[]; indexRows?: IndexRowBatch; }): Promise; pruneExpiredSignRequests(now: number): Promise; getSignRequest(cid: string, now: number): Promise; putSignRequest(request: StoredSignRequest, now: number): Promise<'created' | 'identical' | 'conflict' | 'capacity'>; listPendingSignRequests(params: { subjectDID: string; after?: string; limit: number; now: number; }): Promise<{ requests: StoredSignRequest[]; next: string | null; } | null>; putSignResponse(cid: string, response: string, now: number): Promise<'created' | 'identical' | 'conflict' | 'not-found'>; declineSignRequest(cid: string, now: number): Promise<'declined' | 'responded' | 'not-found'>; getOperation(cid: string): Promise; private putOperation; getIdentityChain(did: string): Promise; private putIdentityChain; getContentChain(contentId: string): Promise; private putContentChain; getBlob(key: BlobKey): Promise; private putBlob; getCountersignatures(operationCID: string): Promise; private addCountersignature; private addRevocation; isCredentialRevoked(issuerDID: string, credentialCID: string, asOfUnix?: number): Promise; getRevocationForCredential(credentialCID: string): Promise; getRevocationsByIssuer(issuerDID: string): Promise; queryIndexIdentities(q: { did?: string; key?: string; hasPublicProfile?: boolean; nameContains?: string; after?: string; orderedAfter?: IndexOrderedCursor; order?: IndexOrder; limit: number; }): Promise; queryIndexContent(q: { contentId?: string; creator?: string; signer?: string; docSchema?: string; documentCID?: string; publicRead?: boolean; isDeleted?: boolean; titleContains?: string; after?: string; orderedAfter?: IndexOrderedCursor; order?: IndexOrder; limit: number; }): Promise; queryIndexCredits(q: { did?: string; contentId?: string; role?: string; after?: IndexCreditCursor; limit: number; }): Promise; queryIndexArtifacts(q: { cid?: string; signer?: string; docSchema?: string; after?: string; orderedAfter?: IndexOrderedCursor; order?: IndexRecencyOrder; limit: number; }): Promise; queryIndexCountersignatures(q: { witness: string; relation?: string; after?: string; orderedAfter?: IndexOrderedCursor; order?: IndexRecencyOrder; limit: number; }): Promise; queryIndexCredentials(q: { issuer?: string; resource?: string; action?: string; after?: string; orderedAfter?: IndexOrderedCursor; order?: IndexRecencyOrder; limit: number; }): Promise; private putIndexIdentityRow; private putIndexContentRow; private putIndexCreditRows; private putIndexArtifactRow; private putIndexContentSigner; private putIndexIdentityKey; private putIndexOperationSignerKey; private putIndexCountersignatureRow; queryIndexOperations(q: { kind?: OperationKind; chainId?: string; signerKey?: string; orderedAfter?: IndexOrderedCursor; order: IndexRecencyOrder; limit: number; }): Promise; getIndexIdentityDIDsByProfileAnchor(contentId: string): Promise; getIndexContentIdsByDocumentCID(documentCID: string): Promise; getPublicCredentials(resource: string): Promise; getPublicCredentialByCID(cid: string): Promise; private addPublicCredential; /** * ISSUER-SCOPED. A revocation only reaches the credentials its own signer * issued; a held grant issued by someone else is left alone. Without the * pairing, any identity could un-publish anyone's public content by signing a * revocation that named its credential CID. */ private removeIssuerPublicCredential; private appendToLog; readLog(params: { after?: string; limit: number; }): Promise<{ entries: LogEntry[]; next: string | null; } | null>; getStats(): Promise; getIdentityStateAtCID(did: string, cid: string): Promise<{ state: VerifiedIdentity; lastCreatedAt: string; } | null>; getContentStateAtCID(contentId: string, cid: string): Promise<{ state: VerifiedContentChain; lastCreatedAt: string; } | null>; getPeerCursor(peerUrl: string): Promise; setPeerCursor(peerUrl: string, cursor: string): Promise; private rawOps; private rawOpSeq; putRawOp(cid: string, jwsToken: string, origin?: OpOrigin): Promise; getUnsequencedOps(after: string, limit: number): Promise; markOpsSequenced(cids: string[]): Promise; markOpRejected(cid: string, _reason: string): Promise; countUnsequenced(): Promise; resetSequencer(): Promise; } type AdmissionMode = 'current' | 'historical'; /** * Derive the operation CID from a JWS token by re-encoding the decoded payload. * Returns the empty string for an undecodable token. Used at verify-failure * sites so a rejection still carries a CID and can be durably rejected by the * sequencer (instead of being skipped forever by `if (!res.cid) continue`). */ declare const computeOpCID: (jwsToken: string) => Promise; /** * A STORE OPERATION FAILED. Not a verdict about the operation — a fact about the * store. * * The distinction is destructive to get wrong. A rejection the sequencer reads * as permanent DELETES the raw op, which is the only copy the relay holds, so a * momentary "database is locked" during signature verification used to destroy a * valid operation for good. And a read that fails is not a read that found * nothing: `getOperation` throwing and `getOperation` returning `undefined` mean * opposite things, and treating the first as the second re-runs a genesis branch * over a chain that already has history. * * So every store call ingestion makes goes through `failClosedStore`, which tags * a failure with this type, and every classification site reads the tag back: * a store fault is RETRYABLE, the raw op is kept, and the same work happens * again on the next pass once the store is well. Nothing infers it from a * message. */ declare class StoreFaultError extends Error { readonly site: string; readonly fault: unknown; constructor(site: string, fault: unknown); } declare const isStoreFault: (error: unknown) => boolean; /** * The identity's verified state AS OF `basis`, from this store's copy of its * chain (PROTOCOL, Time basis). * * TWO BRANCHES, ONE ANSWER. When the stored chain's last operation is dated at * or before the basis, head state IS the state as of the basis for the log this * store holds, and no walk runs. Otherwise the log is re-verified with the * basis, which folds the prefix the basis names. Both branches return the same * key set for the same basis. * * ONLY THE RE-WALK BRANCH IS DETERMINATE, and the answer carries which one it * is. A stored operation dated after the basis proves this store holds every * operation the basis names: the chain is linear and its `createdAt` strictly * increases, so anything still to arrive is dated after the stored head. A chain * that ends at or before the basis proves nothing of the sort, because the next * operation to arrive can still be dated at or before the basis and add a key. * A key missing from an indeterminate answer is a dependency miss rather than a * verdict (`basisDeterminate`). * * DELETION READS HEAD STATE, never the as-of state: a deleted issuer's * credentials are invalid retroactively, so the deletion a later operation * recorded reaches back past the basis (CREDENTIALS, Deleted issuers). * * An unknown chain is a retryable miss, exactly as elsewhere. A basis earlier * than the chain's genesis is a verdict: the identity had no state then. */ declare const resolveIdentityAsOf: (store: RelayReadStore, did: string, basis?: string) => Promise; /** * Create an identity resolver that answers at the basis it is given, and at head * state when it is given none (an ephemeral presentation, whose basis is now). */ declare const createIdentityResolver: (store: RelayReadStore) => (did: string, basis?: string) => Promise; /** * Create a key resolver that resolves a kid in the identity's EFFECTIVE state as * of the basis it is handed — the state that held when the artifact was signed. * * A key absent from that state is a VERDICT only when the answer is determinate: * the stored chain runs past the basis, so no operation the basis names can * still arrive. Otherwise the miss is retryable, because sync may still deliver * the operation that adds the key, and a verdict DELETES the raw op. An unknown * chain stays retryable for the same reason. A malformed DID is a verdict at * once, mirroring the Go twin's `dfos.ValidateDID`. */ declare const createAsOfKeyResolver: (store: RelayReadStore) => (kid: string, basis?: string) => Promise; /** * Create a key resolver that only resolves current-state keys. * * Used for live authentication and first admission — the freshness question the * relay asks of a NEW operation (RELAY, "Ingest asks freshness"). The basis is * accepted and ignored: this resolver answers about head state by construction. */ declare const createCurrentKeyResolver: (store: RelayReadStore) => (kid: string, _basis?: string) => Promise; /** * Ingest a batch of JWS operations. * * Classifies, dependency-sorts, and processes each token. Identity operations * are processed first so content chains can resolve their keys; within each * kind, genesis operations are processed before extensions. Each accepted * operation lands as ONE `commit`. * * NOTHING HERE MAINTAINS THE INDEX. The `/index/v0` projection is a worker over * the operation log (`projectIndex`), driven by its own cursor and its own * budget. Ingestion's job ends at the commit. */ /** * Runs under the store's chain-state lock. The entry point for every caller that * does not already hold it. */ declare const ingestOperations: (tokens: string[], writeStore: RelayWriteStore, options?: { logEnabled?: boolean; admissionMode?: AdmissionMode; }) => Promise; /** * Returns true if a rejection must NOT be treated as permanent. * * Two structured signals, both set by the ingest producer, neither inferred from * the human-readable `error` string: a missing dependency that may arrive later * via sync or gossip, and a store fault, which is not a verdict about the * operation at all. A permanent rejection DELETES the raw op — the only copy the * relay holds — so both have to be readable as facts rather than as phrases. */ declare const isRetryableRejection: (res: Pick) => boolean; /** * Process unsequenced raw ops in a fixed-point loop until no more progress * is made, under the store's chain-state lock. Returns the JWS tokens of newly * sequenced ops and aggregate stats. * * Twin of Go's `RunSequencer`, which takes `ingestMu` and delegates to * `runSequencerLocked`. A caller already holding the lock calls * `sequenceOpsLocked` instead — the lock is not reentrant. */ declare const sequenceOps: (store: RelayWriteStore & RelayWriterState) => Promise<{ newOps: string[]; result: SequenceResult; }>; export { BlobKey, CommitBatch, CommitResult, type CreatedRelay, DEFAULT_INDEX_PROJECTION_BUDGET, type DidDocument, type DidDocumentMetadata, type DidResolutionResult, type DidServiceEntry, type DidVerificationMethod, IndexArtifactRow, IndexContentRow, IndexCreditRow, IndexCursor, IndexIdentityRow, IndexOperationRow, type IndexProjectionOptions, type IndexProjectionRun, type IndexProjectionStore, IndexReadStore, IndexRecencyOrder, IndexRowBatch, IndexWriteStore, IngestionResult, LogEntry, MemoryRelayStore, OperationKind, RelayIdentity, RelayOptions, RelayReadStore, RelayStats, RelayWriteStore, RelayWriterState, SequenceResult, SigningStore, StoreFaultError, StoredContentChain, StoredIdentityChain, StoredOperation, StoredRevocation, StoredSignRequest, bootstrapRelayIdentity, bootstrapRelayIdentityFromKey, chunkOps, computeOpCID, createAsOfKeyResolver, createCurrentKeyResolver, createIdentityResolver, createRelay, drainIndexProjection, identityToDidDocument, ingestOperations, isRetryableRejection, isStoreFault, isValidDfosDid, projectIndex, projectIndexAfterBlob, resolveDidDocument, resolveIdentityAsOf, sequenceOps };