Federation Trust Components
What this page is
Rendered compatibility entry point for the four federation trust component specs: Spec-04-Manifests, Spec-05-Federation-Trust, Spec-06-Capability-Tokens, and Spec-08-Quarantine-Garden. Org manifests, capability tokens, source-trust score, quarantine garden, recall-time sanitizer.
Authoritative source:
spec/stigmem-spec-v0.9.0a1.md
Each subsection below shows the most recent normative text from the spec source.
This section is non-normative.
The active security policy — supported versions, vulnerability
reporting instructions, scope definitions, and the coordinated
disclosure timeline — is maintained in
SECURITY.md
at the root of the repository.
Reporting.
Do not open a public GitHub issue for security vulnerabilities. Report via the GitHub private advisory path. We acknowledge within 48 hours and target a patch within 14 days for critical vulnerabilities.
Disclosure timeline: 90 days from the report date before public disclosure, except for vulnerabilities already being actively exploited in the wild.
For the current security posture and Dependabot alert triage covering the pre-reset v1.0-rc snapshot, see the Security Posture section of SECURITY.md.
v1.0 — Stable. All sections normative. Apache-2.0.
§19.1 Org manifest
§19.1.1 Purpose
An org manifest is a signed document that declares the canonical public key for a Stigmem node or organisation and the set of entity URIs that the manifest is authoritative for. Peers MUST use the manifest public key to verify capability tokens (§19.3), provenance signatures (§19.6), and recall-time sanitizer trust decisions (§19.7).
§19.1.2 Manifest fields
The OrgManifest struct carries everything a verifier needs to
validate tokens and provenance from a given node.
OrgManifest:
manifest_version: integer // MUST be 1 for this spec version
entity_uri: URI // root entity URI; MUST be a stigmem:// URI
public_key: base64url // Ed25519 public key (32 bytes, encoded)
key_id: hex // SHA-256 of the 32-byte raw Ed25519 public key
entities: [URI] // entity URIs this manifest is authoritative for; MUST include entity_uri
rotation_events: [RotationEvent] // ordered history of key rotations (§19.1.4); empty on first publish
issued_at: RFC3339 // issuance timestamp
expires_at: RFC3339 // expiry; MUST be at least 24h after issued_at
signature: base64url // Ed25519 sig over the canonical JSON encoding of all other fields
A manifest MUST be self-consistent.
The signature MUST verify under the
public_key declared in the same manifest. The
expires_at ceiling forces regular re-publication,
limiting the window during which a compromised key remains trusted.
§19.1.3 Canonical encoding
Manifest signing and verification MUST use RFC 8785 (JSON
Canonicalization Scheme, JCS) for deterministic byte ordering.
Implementations MUST serialize the manifest body (all fields except
signature) using JCS before signing. Implementations MUST reject
manifests where JCS canonicalization of the non-signature fields
does not reproduce the same bytes that were signed.
§19.1.4 Key rotation
Key rotation events allow a node to cycle its signing key while preserving a verifiable chain of custody back to its original published key.
RotationEvent:
rotated_at: RFC3339 // timestamp of rotation
old_key_id: hex // key_id of the previous key
new_key_id: hex // key_id of the new key (= current manifest's key_id)
rotation_sig: base64url // Ed25519 sig over canonical JSON of { entity_uri, old_key_id, new_key_id, rotated_at }
// signed by the OLD private key; entity_uri binds the event to its manifest
The rotation_sig MUST verify under the public key identified by
old_key_id. This creates an unbroken chain: the previous key
vouches for the new key.
Rotation chain invariants:
rotation_eventsMUST be ordered chronologically ascending.- Each event's
old_key_idMUST equal thekey_idof the preceding entry (or the original manifest'skey_idfor the first rotation). - A valid rotation chain MUST terminate with the
new_key_idmatching the current manifest'skey_id. - The
rotation_eventscount in a newly published manifest MUST be ≥ the count in the most recently submitted manifest for the sameentity_uri. Peers MUST reject any manifest where the rotation event count regresses.
§19.1.5 Entity URI list
The entities array declares which entity URIs this manifest speaks
for. An entity URI MUST appear in at most one valid (non-expired)
manifest per transparency log epoch. Nodes MUST reject capability
tokens and provenance signatures claiming to be from an entity URI
that does not appear in the signer's manifest.
§19.1.6 Manifest publication
Nodes MUST publish their manifest at
/.well-known/stigmem-manifest.json. Nodes SHOULD also submit each
new or rotated manifest to the transparency log (§19.2) for
independent auditability. A manifest MUST be re-submitted on key
rotation.
§19.2 Transparency log integration
§19.2.1 Purpose
A transparency log provides tamper-evident, append-only evidence that a manifest was published at a given time and has not been backdated or silently revoked. It is the audit anchor for the federation trust model.
§19.2.2 Recommended integration
Implementations SHOULD integrate with Rekor (Sigstore's transparency log) or an equivalent OSS log offering:
Append-only, tamper-evident
Backed by a Merkle tree.
Public inclusion proofs
Using signed tree heads (STH).
HTTP API
For entry submission and proof retrieval.
Implementations MAY operate a self-hosted Rekor instance. A self-hosted log is acceptable for private deployments but SHOULD be independently accessible to all federation peers.
§19.2.3 What we depend on vs. require
Nodes MUST NOT trust a peer's manifest without a valid inclusion
proof when operating in trust_mode: strict (see §19.4.3). Nodes
operating in trust_mode: relaxed MAY accept peer manifests without
log verification, but SHOULD log a warning.
§19.2.4 Inclusion proof format
When submitting a manifest to the log, the node receives a
LogEntry:
LogEntry:
log_id: hex // transparency log's identity
log_index: integer // position in the log
integrated_time: RFC3339 // when the entry was appended
entry_hash: hex // SHA-256 of the log entry body
signed_entry_ts: base64url // signed timestamp from the log (Rekor: SignedEntryTimestamp)
inclusion_proof: {
log_index: integer,
root_hash: hex, // Merkle root of the log at time of proof
tree_size: integer,
hashes: [hex], // sibling hashes for Merkle path
checkpoint: string // signed tree head
}
Nodes SHOULD store the LogEntry alongside the manifest and serve
it at /.well-known/stigmem-manifest-proof.json. Peers MUST be able
to verify the inclusion proof independently using only the log's
public key and the proof data.
§19.2.5 Revocation events
Capability token revocations (§19.3.4) MUST be submitted to the transparency log as a distinct log entry type. This makes revocations independently auditable: a peer can verify a token is revoked by checking the log without trusting the issuing node's runtime state.
§19.2.6 Checkpoint verification
The checkpoint field in LogEntry.inclusion_proof is a signed
note in the
transparency-dev/formats
checkpoint format.
- Key discovery. Obtain the log's public key from the log's key discovery endpoint. For Rekor-compatible logs, issue
GET /api/v1/logagainst the log instance; the response includes the ECDSA public key (PEM-encoded in thepublicKey.contentfield, base64-encoded). - Verification. For Rekor-compatible logs, implementations MUST verify the checkpoint using the log's published public key. A checkpoint that fails signature verification MUST cause the enclosing inclusion proof to be rejected.
- Failure-closed behavior. If the transparency log is unreachable when an inclusion proof is required (i.e., the node operates in
trust_mode: strict), the manifest MUST be rejected. Implementations MUST NOT fall back to accepting an unverified manifest. - Reference implementation. The
sigstore-pythonlibrary (sigstore.verify) is the reference for checkpoint and inclusion-proof verification. - Error codes. See §19.9 for
inclusion_proof_invalid(HTTP 400) andtransparency_log_unavailable(HTTP 503).
§19.3 Capability tokens
§19.3.1 Purpose
A capability token is a signed, short-lived credential that grants a specific named permission to a specific subject from a specific issuer. Tokens replace ad-hoc per-peer trust agreements with a verifiable, revocable, auditable delegation primitive.
§19.3.2 Token shape
CapabilityToken:
token_version: integer // MUST be 1 for this spec version
token_id: UUID // unique identifier; used for revocation lookup
issuer: URI // entity URI of the issuing node/org (MUST be in issuer's manifest)
subject: URI // entity URI of the token bearer
verb: string // one of: "read" | "write" | "admin" | "federate" | "subscribe" | "tombstone:read"
object: URI // resource the verb applies to (scope URI, garden URI, or "*" for any)
issued_at: RFC3339
expiry: RFC3339 // MUST be set; MUST NOT exceed 90 days from issued_at
nonce: hex // 32 bytes cryptographically random; prevents replay
signature: base64url // Ed25519 sig over canonical JSON of all other fields, signed by issuer key
The verb values are:
readobjectwriteobjectadminobjectfederatesubscribeobject (scope URI or entity URI).tombstone:read§19.3.3 Signing and verification
The issuer MUST sign the token using the private key corresponding
to the public_key in their current org manifest (§19.1).
Verifiers MUST:
- Resolve the issuer's org manifest.
- Check
manifest.expires_at > now; if expired, attempt refresh from the issuer's/.well-known/stigmem-manifest.json; reject the token if the manifest is still expired after refresh. - Verify the manifest's self-signature.
- Verify the token's
signatureunder the manifest'spublic_key. - Check that
subjectappears in the issuer'sentitieslist. External-entity subjects are not permitted; cross-org delegation requires the delegatee to obtain their own org manifest and capability tokens. - Check
expiry > now. - Check
expiry ≤ issued_at + 90 days. - Check the token is not revoked (§19.3.4).
A token that fails any of these steps MUST be rejected.
§19.3.4 Revocation
Issuers MAY revoke a token before its expiry by submitting a revocation event to the transparency log (§19.2.5) and calling the local revocation API (§5.24).
RevocationEvent:
event_type: "token_revocation"
token_id: UUID // the token being revoked
issuer: URI
revoked_at: RFC3339
reason: string // human-readable; SHOULD be informative
signature: base64url // Ed25519 sig over canonical JSON of other fields
Nodes that receive a token MUST check for a revocation event before honoring it. Nodes SHOULD cache revocation events with a TTL of no less than 60 seconds. A revoked token MUST be rejected even if it has not yet expired.
Revocation transparency log entries are for auditability, not real-time validation.
Implementations MUST NOT attempt an inline transparency log query as part of per-request token validation; doing so would introduce a synchronous dependency on an external service in the hot path. Real-time revocation checks MUST use the local revocation cache (populated by background sync) and the issuer's revocation API (§5.24).
§19.3.5 Token nonce and replay prevention
The nonce field MUST be 32 bytes of cryptographically random data
(e.g., from /dev/urandom). Receivers MUST maintain a nonce cache
in trust_mode: strict; receivers SHOULD maintain a nonce cache in
trust_mode: relaxed. The nonce cache MUST be global (keyed on the
nonce value alone, not per-issuer or per-subject) and MUST cover the
token's full validity window (from issued_at to expiry). A
nonce that appears in the cache MUST cause the token to be rejected
with a token_replay error.
§19.4 Source-trust score
§19.4.1 Purpose
The source-trust score t is a scalar in [0.0, 1.0] that expresses
how much confidence a node should place in facts asserted by a
given source URI. It modulates the effective confidence of recalled
facts: effective_confidence = fact.confidence × t.
§19.4.2 Derivation formula
t = clamp(
w_i × identity_strength(source)
+ w_p × peer_history(source)
+ w_s × scope_authority(source, fact.scope)
+ w_a × attestation_mode_factor(node.attestation_mode),
0.0, 1.0
)
Default weights: w_i = 0.35, w_p = 0.30, w_s = 0.25, w_a = 0.10.
These MUST be documented in the node's /.well-known/stigmem
response as trust_weights if non-default values are used.
Component definitions:
identity_strength(source) — a float in [0.0, 1.0] measuring how
strongly the source is identified:
entity_uri registered on local API keypeer_history(source) — derived from the node's interaction history:
scope_authority(source, scope):
verb: writefederate tokenattestation_mode_factor(mode):
enforcewarnoff§19.4.3 Trust mode configuration
Configure via STIGMEM_TRUST_MODE=strict|relaxed|off.
strictsource_trust is computed for all inbound facts; facts from sources with t < 0.2 are quarantined (§19.5).relaxed (default)source_trust but MUST NOT quarantine facts based solely on a low score; attestation failures are logged.offsource_trust is not computed; source_trust field is null on all stored facts.§19.4.4 Recall-time multiplier
At recall time, the effective_confidence of a fact is computed as:
effective_confidence = fact.confidence × t(fact.source)
Where t(fact.source) is recomputed live at recall time using
current peer state (not the stored source_trust snapshot).
Implementations SHOULD cache per-source trust scores with a TTL of
no less than 60 seconds.
Recall results SHOULD include effective_confidence alongside
confidence when trust_mode is strict or relaxed. Callers
MUST NOT rely on effective_confidence being identical to
confidence.
The default source-trust cache is per-worker and in-memory. In
multi-worker deployments (gunicorn, uvicorn --workers), each
worker holds an independent cache. Cache misses and stale entries
can cause non-deterministic trust scoring. For production
multi-worker deployments, configure
STIGMEM_TRUST_CACHE_BACKEND=redis and provide
STIGMEM_REDIS_URL. This will be required in a future release.
§19.4.5 Bounds and defaults
Always clamped
t is always clamped to [0.0, 1.0].
No-component default
A source with no computable score (all components unavailable) MUST default to t = 0.5.
Blocklist override
Admin blocklisted sources MUST return t = 0.0 regardless of other components.
§19.5 Quarantine garden
§19.5.1 Purpose
A quarantine garden is a special-purpose Memory Garden (§17) that holds facts pending human or automated review before they are integrated into production scope.
§19.5.2 Relationship to §17 garden machinery
A quarantine garden is a Garden record (§17) with an additional
quarantine: true flag set at creation time. It inherits all §17
mechanics: it is scope-bound, ACL-controlled, facts within it are
isolated from federation, and standard garden CRUD applies.
Differences from a standard garden:
- A quarantine garden adds a
quarantine:moderatorrole (§19.5.3) not present in standard gardens. - Facts in a quarantine garden MAY NOT be asserted directly — they are only populated by the node's automatic quarantine policy (§19.5.4) or by explicit operator action.
- A quarantine garden MUST NOT be deleted while it holds unreviewed facts (status
pending). Attempting deletion returns HTTP 409quarantine_has_pending_facts.
§19.5.3 Roles
adminquarantine:moderatorwriterreaderThe node MUST automatically add the garden's creating principal as
admin. A quarantine garden MUST have at least one admin or
quarantine:moderator at all times.
§19.5.4 Automatic quarantine policy
When trust_mode: strict is set (§19.4.3), the node MUST route
inbound federated facts to the node's designated quarantine garden
when:
Low trust score
The fact's source has source_trust t < 0.2.
Missing manifest
The fact's source lacks a valid org manifest.
Provenance failure
The fact fails provenance chain verification (§19.6.3).
When no quarantine garden has been designated, the node MUST reject
these facts with HTTP 403 trust_below_threshold rather than
silently dropping them.
The node MAY also quarantine facts that trigger the recall-time
sanitizer (§19.7) in quarantine enforcement mode.
Enabling trust_mode=strict without configuring a quarantine
garden will cause all low-trust facts (score < 0.2) to be
permanently rejected with 403 trust_below_threshold. Before
setting trust_mode=strict, create a quarantine garden and set
quarantine_garden_id in your federation config. A future release
will add a startup check that enforces this.
§19.5.5 Promote and reject mechanics
Promote. A quarantine:moderator or admin calls
POST /v1/gardens/:id/promote (§5.25) with a target_garden_id.
The node:
- Moves the fact's
garden_idto thetarget_garden_id(or clears it for no-garden). - Removes the
quarantine:pendingstatus marker from the fact. - Logs a
quarantine_promoteevent to the attestation audit log.
Reject. A quarantine:moderator or admin calls
POST /v1/gardens/:id/reject (§5.25). The node:
- Sets
confidence = 0.0on the fact (logical retraction). - Sets a
quarantine:rejectedmarker. - Logs a
quarantine_rejectevent to the attestation audit log.
A rejected fact is retained in the quarantine garden for audit purposes. It MUST NOT be served in normal recall results. It MUST be visible to garden admins and moderators via the garden-filtered fact query.
§19.5.6 Auditability
All promote and reject events MUST be written to the attestation audit log (§18.10) with the additional fields:
quarantine_action: "promote" | "reject"
quarantine_garden_id: <garden_id>
target_garden_id: <garden_id> | null // promote only
reason: string
acted_by: <entity_uri>
acted_at: RFC3339
The audit log MUST be queryable by quarantine_garden_id and
quarantine_action.
§19.6 Provenance chain
§19.6.1 Purpose
The provenance chain allows a fact to declare its intellectual
antecedents (derived_from) and carry cryptographic attestations
from intermediate processors (attestation_chain). Together they
create a verifiable lineage from source to recall, enabling
detection of data tampering or unexplained derivation gaps.
§19.6.2 Fact hash computation
A fact hash is the hex-encoded SHA-256 digest of the fact's canonical JSON representation. The canonical form MUST be JCS (RFC 8785) of:
{
"entity": "<entity>",
"relation": "<relation>",
"value": <FactValue>,
"scope": "<scope>",
"source": "<source>",
"confidence": <float>,
"ts": "<RFC3339>"
}
The following fields MUST be excluded from the hash input: id,
garden_id, attested, source_trust, derived_from,
attestation_chain. This ensures the hash is stable regardless of
storage metadata or trust annotations.
§19.6.3 Provenance field shapes
derived_from: [FactHash]
64-char lowercase hex
Each FactHash MUST be a 64-character lowercase hex string.
Logical derivation order
Array ordered by logical derivation precedence (first = most direct antecedent).
Empty = null
An empty array is equivalent to null (no declared provenance).
DAG required
The reference graph MUST be a DAG. Implementations MUST detect circular references using a visited-set during traversal and MUST reject any fact that would create a cycle with HTTP 400 provenance_cycle_detected.
Resolvable references
The referenced facts MUST either exist in the same node or be resolvable via federation. Nodes SHOULD validate at write time in trust_mode: strict; in relaxed, MAY log a warning for dangling references.
attestation_chain: [Signature]
Ed25519 sigs over canonical hash
Each Signature is a base64url-encoded Ed25519 signature over the canonical fact hash.
Innermost-to-outermost order
Signatures are ordered from innermost processor to outermost (first processor = index 0).
Max 16 entries
attestation_chain MUST NOT exceed 16 entries. Nodes MUST reject facts with longer chains with HTTP 400 attestation_chain_too_long.
Parallel issuer URIs
Each signature is bound to a specific entity URI: the signing entity MUST include their entity URI in a parallel attestation_chain_issuers: [URI] field (same indexing). Implementations MUST include both fields together or neither.
Verify each signature
Verifiers MUST verify each signature in the chain using the corresponding issuer's manifest public key. A chain with an invalid signature at any position MUST be rejected entirely.
§19.6.4 Verification rules
A provenance chain is valid if all of the following hold.
- Every
FactHashinderived_fromreferences a fact that exists and whose stored hash matches the declared value. - Every signature in
attestation_chainverifies under the corresponding issuer's current manifest public key. - No issuer in
attestation_chain_issuersappears more than once (no circular attestation). - The issuers' entity URIs each appear in their respective manifest's
entitieslist. - If any field included in the fact hash is modified after attestation, the node MUST either: (a) clear
attestation_chainandattestation_chain_issuers(treating the updated fact as unattested), or (b) reject the update with HTTP 409fact_is_attested. Nodes MUST NOT retain stale attestation chains after hash-relevant field changes.
Nodes MUST reject facts with invalid provenance chains in
trust_mode: strict. Nodes SHOULD log warnings for invalid chains
in trust_mode: relaxed.
§19.7 Recall-time content sanitizer
§19.7.1 Purpose
The recall-time content sanitizer prevents prompt-injection payloads and malformed values from reaching the recall layer's consumers. It is a defense-in-depth control applied at recall time, not at write time, so that future policy changes can be retroactively applied to already-stored facts.
The sanitizer is NOT applied at write time (storage is a transparent
record layer). It is applied immediately before facts are serialized
into the API response for GET /v1/facts and GET /v1/recall
endpoints.
§19.7.2 Default-deny sentinel patterns
Before applying sentinel patterns, implementations MUST normalize all string input to Unicode NFKC form. Implementations MUST also strip or reject the following Unicode categories: bidirectional control characters (U+200F, U+200E, U+202A–U+202E, U+2066–U+2069) and invisible formatting characters (U+200B–U+200D, U+FEFF).
The following patterns are checked against all string-typed
FactValue fields:
// Instruction-injection sentinels
\bignore\s+(all\s+)?previous\s+instructions?\b (case-insensitive)
\bdisregard\s+(all\s+)?previous\s+(prompt|instructions?)\b
\byou\s+are\s+now\s+(?:in\s+)?(?:a\s+)?(?:different|new)\s+mode\b
\bact\s+as\s+(?:an?\s+)?(?:evil|unfiltered|uncensored|dan\b)
\bsystem\s+prompt\s*:\s*
\<\|im_start\|\>
\<\|im_end\|\>
\[INST\]
\[\/INST\]
\bHuman:\s* // common chat-template leak
\bAssistant:\s* // ditto
// Schema evasion sentinels
\{\s*"__proto__"\s*: // prototype pollution attempt
\{\s*"constructor"\s*:
This list is the default. Operators MAY add patterns via node
configuration (STIGMEM_SANITIZER_EXTRA_PATTERNS as a
newline-delimited regex file). Operators MUST NOT remove default
patterns in trust_mode: strict.
§19.7.3 Schema enforcement for typed FactValues
stringnumberNaN, +Inf, -Inf MUST be replaced with null.bool0/1 is NOT performed; unexpected values MUST be returned as null.refnull.jsonnull.textstring, plus sentinel pattern matching.Facts where type enforcement produces a null substitution MUST
include a sanitizer_redacted: true marker in the API response
alongside the null value.
§19.7.4 Enforcement modes
Configured via STIGMEM_SANITIZER_MODE.
blocktrust_mode: strict{ "fact_id": "...", "sanitized": true } returned in its position.quarantinewarntrust_mode: relaxedsanitizer_warnings: ["<matched pattern>"]; content unmodified.offtrust_mode: offOperators MAY configure a more restrictive mode than the
trust_mode default. Operators MUST NOT configure a less
restrictive mode in trust_mode: strict.
§19.7.5 Placement and ordering
The sanitizer is the final processing step before serialization in the recall pipeline:
Storage layer
→ Scope/garden ACL filter (§17.3)
→ Source-trust multiplier applied to effective_confidence (§19.4.4)
→ Provenance chain verification (§19.6.4, strict mode only)
→ Content sanitizer ← HERE
→ API response serializer
The sanitizer MUST run after trust scoring and provenance verification so that trust metadata is available to the enforcement decision.
§19.7.6 Audit logging
Every sanitizer action MUST be logged to the attestation audit log (§18.10):
sanitizer_action: "block" | "quarantine" | "warn"
fact_id: <uuid>
matched_pattern: string // the regex that triggered; "schema_enforcement" for type failures
recall_endpoint: string // "/v1/facts" or "/v1/recall"
ts: RFC3339
§19.8 Schema migration (migration 006)
Migration 006 adds three tables to support the federation trust
layer. federation_manifests stores org manifests indexed by
entity_uri and key_id for fast lookup during token verification.
capability_tokens records every token the node has issued or
accepted. source_trust_scores caches computed trust scores per
source URI and scope pair.
-- Org manifest storage
CREATE TABLE IF NOT EXISTS federation_manifests (
id TEXT PRIMARY KEY,
entity_uri TEXT NOT NULL UNIQUE,
manifest_json TEXT NOT NULL, -- JCS-canonical manifest body
signature TEXT NOT NULL, -- base64url Ed25519 sig
key_id TEXT NOT NULL,
issued_at TEXT NOT NULL,
expires_at TEXT NOT NULL,
log_entry_json TEXT, -- NULL if not yet submitted to transparency log
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_federation_manifests_entity_uri ON federation_manifests(entity_uri);
CREATE INDEX IF NOT EXISTS idx_federation_manifests_key_id ON federation_manifests(key_id);
-- Capability token storage
CREATE TABLE IF NOT EXISTS capability_tokens (
id TEXT PRIMARY KEY, -- token_id UUID
token_json TEXT NOT NULL, -- full signed token body (JCS-canonical)
issuer TEXT NOT NULL,
subject TEXT NOT NULL,
verb TEXT NOT NULL,
object TEXT NOT NULL,
issued_at TEXT NOT NULL,
expiry TEXT NOT NULL,
nonce TEXT NOT NULL UNIQUE,
revoked_at TEXT, -- NULL if active
revoke_log TEXT, -- JSON of RevocationEvent if revoked
created_at TEXT NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_capability_tokens_subject ON capability_tokens(subject);
CREATE INDEX IF NOT EXISTS idx_capability_tokens_issuer ON capability_tokens(issuer);
CREATE INDEX IF NOT EXISTS idx_capability_tokens_nonce ON capability_tokens(nonce);
CREATE INDEX IF NOT EXISTS idx_capability_tokens_expiry ON capability_tokens(expiry);
-- Quarantine metadata extension on facts table
ALTER TABLE facts ADD COLUMN IF NOT EXISTS quarantine_status TEXT;
-- NULL = not quarantined; "pending" = awaiting review; "promoted" = approved; "rejected" = rejected
ALTER TABLE facts ADD COLUMN IF NOT EXISTS quarantine_garden_id TEXT REFERENCES gardens(id);
ALTER TABLE facts ADD COLUMN IF NOT EXISTS quarantine_acted_by TEXT;
ALTER TABLE facts ADD COLUMN IF NOT EXISTS quarantine_acted_at TEXT;
ALTER TABLE facts ADD COLUMN IF NOT EXISTS quarantine_reason TEXT;
-- Provenance chain fields on facts table
ALTER TABLE facts ADD COLUMN IF NOT EXISTS derived_from TEXT; -- JSON array of FactHash
ALTER TABLE facts ADD COLUMN IF NOT EXISTS attestation_chain TEXT; -- JSON array of base64url sigs
ALTER TABLE facts ADD COLUMN IF NOT EXISTS attestation_chain_issuers TEXT; -- JSON array of URI
-- Source trust snapshot
ALTER TABLE facts ADD COLUMN IF NOT EXISTS source_trust REAL;
CREATE INDEX IF NOT EXISTS idx_facts_quarantine_status ON facts(quarantine_status) WHERE quarantine_status IS NOT NULL;
CREATE INDEX IF NOT EXISTS idx_facts_quarantine_garden ON facts(quarantine_garden_id) WHERE quarantine_garden_id IS NOT NULL;
§19.9 Error reference
manifest_signature_invalidsignature does not verify under public_key.manifest_rotation_chain_invalidtoken_nonce_invalidprovenance_hash_invalidderived_from entry is not a 64-char lowercase hex string.provenance_cycle_detectedderived_from graph contains a cycle.attestation_chain_too_longattestation_chain exceeds 16 entries.inclusion_proof_invalidtrust_below_thresholdt < 0.2 in trust_mode: strict and no quarantine garden configured.token_expiredexpiry has passed.token_revokedtoken_replayinsufficient_capabilityentity_not_in_manifestentities list.quarantine_has_pending_factsfact_not_quarantine_pendingpending quarantine state.fact_is_attestedattestation_chain is present and node configured to reject rather than clear.attestation_chain_mismatchattestation_chain and attestation_chain_issuers array lengths differ.transparency_log_unavailabletrust_mode: strict, the manifest MUST be rejected.§19.10 Well-known advertisement
Nodes MUST extend their /.well-known/stigmem response to include
federation trust configuration:
{
...existing fields...,
"federation_trust": {
"trust_mode": "strict" | "relaxed" | "off",
"sanitizer_mode": "block" | "quarantine" | "warn" | "off",
"manifest_url": "https://node.example.com/.well-known/stigmem-manifest.json",
"manifest_proof_url": "https://node.example.com/.well-known/stigmem-manifest-proof.json",
"trust_weights": {
"identity_strength": 0.35,
"peer_history": 0.30,
"scope_authority": 0.25,
"attestation_mode": 0.10
}
}
}
trust_weights MUST be included when non-default values are
configured.