Skip to content

vacant.core

Shared primitives that every other module imports — types, constants, crypto, errors. Treat anything here as load-bearing for the public contract.

types

Shared, wire-stable core types.

These are the contract every downstream component (P1-P7) imports. They are deliberately small and frozen-where-possible so that signatures, hash chains, and equality semantics are unambiguous.

Cross-cutting design notes:

  • All cryptographic payloads are hashed/signed over a canonical byte encoding defined here (not Pydantic's JSON output), so re-serialisation through different Pydantic versions cannot break verification.
  • Logbook is the only mutable type — append-only via .append().
  • verify_* methods return bool and never raise on a benign mismatch; callers that want exceptions on failure can wrap with assert or use the verify_or_raise helper from vacant.core.crypto.

EMPTY_PREV_HASH module-attribute

EMPTY_PREV_HASH: bytes = b'\x00' * HASH_DIGEST_BYTES

Sentinel prev_hash used by the genesis (first) LogEntry.

VacantId

Bases: BaseModel

Numerical-sameness identity (Ricoeur idem) — wraps an Ed25519 pubkey.

A VacantId is the unchanging part of a vacant: the same bytes always refer to the same vacant, across substrate changes, rotations, graduation, and SUNK transitions. Equality and hashing are derived from pubkey_bytes so VacantId is safe to use as a dict key or set member.

Attributes:

Name Type Description
pubkey_bytes bytes

Raw 32-byte Ed25519 public key. Validated at construction time; lengths other than ED25519_PUBLIC_KEY_BYTES raise ValidationError.

from_verify_key classmethod

from_verify_key(vk: VerifyKey) -> VacantId

Construct a VacantId from a nacl.signing.VerifyKey.

Parameters:

Name Type Description Default
vk VerifyKey

An Ed25519 verify key (pubkey side of a SigningKey).

required

Returns:

Type Description
VacantId

A new VacantId whose pubkey_bytes are the raw 32 bytes

VacantId

of vk.

Source code in src/vacant/core/types.py
@classmethod
def from_verify_key(cls, vk: VerifyKey) -> VacantId:
    """Construct a `VacantId` from a `nacl.signing.VerifyKey`.

    Args:
        vk: An Ed25519 verify key (pubkey side of a `SigningKey`).

    Returns:
        A new `VacantId` whose `pubkey_bytes` are the raw 32 bytes
        of `vk`.
    """
    return cls(pubkey_bytes=bytes(vk))

verify_key

verify_key() -> VerifyKey

Re-derive the VerifyKey for cryptographic verification.

Returns:

Type Description
VerifyKey

The nacl.signing.VerifyKey corresponding to this id's

VerifyKey

pubkey_bytes. Use this to call verify() against any

VerifyKey

signature claimed to be from this vacant.

Source code in src/vacant/core/types.py
def verify_key(self) -> VerifyKey:
    """Re-derive the `VerifyKey` for cryptographic verification.

    Returns:
        The `nacl.signing.VerifyKey` corresponding to this id's
        `pubkey_bytes`. Use this to call `verify()` against any
        signature claimed to be from this vacant.
    """
    return pubkey_from_bytes(self.pubkey_bytes)

hex

hex() -> str

Hex-encode the full 32-byte public key.

Returns:

Type Description
str

64-character lowercase hex string.

Source code in src/vacant/core/types.py
def hex(self) -> str:
    """Hex-encode the full 32-byte public key.

    Returns:
        64-character lowercase hex string.
    """
    return self.pubkey_bytes.hex()

short

short() -> str

Hex prefix suitable for log lines / dashboards.

Returns:

Type Description
str

The first 12 hex characters of hex(). Not collision-free;

str

never use as an equality key.

Source code in src/vacant/core/types.py
def short(self) -> str:
    """Hex prefix suitable for log lines / dashboards.

    Returns:
        The first 12 hex characters of `hex()`. Not collision-free;
        never use as an equality key.
    """
    return self.hex()[:12]

LogEntry

Bases: BaseModel

A single signed line in a vacant's logbook (Ricoeur ipse).

Each entry carries a kind tag (e.g. "BIRTH", "SPAWN", "REVIEW_EVENT"), a UTC timestamp, an arbitrary JSON-serialisable payload, the BLAKE2b-256 hash of the previous entry, and an Ed25519 signature over the canonical concatenation of the four. The hash chain is what gives the logbook its tamper-evident property; the signatures pin authorship.

Attributes:

Name Type Description
kind str

Non-empty event tag.

ts datetime

UTC timestamp; tz-naive datetimes are coerced to UTC.

payload dict[str, Any]

JSON-serialisable dict. Encoded canonically (sorted keys, no whitespace) before hashing.

prev_hash bytes

32-byte BLAKE2b-256 of the previous entry's signing_payload(). Genesis entries use EMPTY_PREV_HASH.

signature bytes

Ed25519 signature over signing_payload().

signing_payload

signing_payload() -> bytes

Canonical bytes that get signed and hashed.

Concatenates kind | ts (UTC ISO-8601) | canonical_json(payload) | prev_hash, joined with the ASCII 0x1f separator. signature itself is not part of the payload — it is the output of signing it.

Returns:

Type Description
bytes

The exact byte-string fed to sign() and verify().

Source code in src/vacant/core/types.py
def signing_payload(self) -> bytes:
    """Canonical bytes that get signed and hashed.

    Concatenates `kind | ts (UTC ISO-8601) | canonical_json(payload)
    | prev_hash`, joined with the ASCII `0x1f` separator.
    `signature` itself is *not* part of the payload — it is the
    output of signing it.

    Returns:
        The exact byte-string fed to `sign()` and `verify()`.
    """
    parts = [
        self.kind.encode("utf-8"),
        _utc_iso(self.ts).encode("utf-8"),
        _canonical_json(self.payload),
        self.prev_hash,
    ]
    return b"\x1f".join(parts)

compute_hash

compute_hash() -> bytes

BLAKE2b-256 over signing_payload().

Returns:

Type Description
bytes

The 32-byte digest used as the next entry's prev_hash.

Source code in src/vacant/core/types.py
def compute_hash(self) -> bytes:
    """BLAKE2b-256 over `signing_payload()`.

    Returns:
        The 32-byte digest used as the *next* entry's `prev_hash`.
    """
    return hash_blake2b(self.signing_payload())

verify

verify(pubkey: VerifyKey) -> bool

Verify signature against pubkey.

Parameters:

Name Type Description Default
pubkey VerifyKey

The Ed25519 verify-key the entry should have been signed under (typically vacant.identity.verify_key()).

required

Returns:

Type Description
bool

True iff the signature is valid over signing_payload().

bool

Never raises on signature mismatch — use verify_or_raise

bool

in vacant.core.crypto if you want exceptions.

Source code in src/vacant/core/types.py
def verify(self, pubkey: VerifyKey) -> bool:
    """Verify `signature` against `pubkey`.

    Args:
        pubkey: The Ed25519 verify-key the entry should have been
            signed under (typically `vacant.identity.verify_key()`).

    Returns:
        `True` iff the signature is valid over `signing_payload()`.
        Never raises on signature mismatch — use `verify_or_raise`
        in `vacant.core.crypto` if you want exceptions.
    """
    return verify(pubkey, self.signing_payload(), self.signature)

Logbook

Bases: BaseModel

Append-only signed history of a vacant's outward behaviour.

The logbook is the only mutable core type — it grows by appending to entries. Existing entries are never edited; tampering with one breaks both the signature and the hash chain to the next entry. Use verify_chain (or the raising variant) to confirm an incoming logbook is intact before consuming it.

Attributes:

Name Type Description
entries list[LogEntry]

Ordered list of LogEntry. Index 0 is genesis; entries[i].prev_hash == entries[i-1].compute_hash() for every i > 0.

latest_hash

latest_hash() -> bytes

Compute the hash that the next entry should use as prev_hash.

Returns:

Type Description
bytes

entries[-1].compute_hash() if the logbook has any entries;

bytes

EMPTY_PREV_HASH (32 zero bytes) otherwise.

Source code in src/vacant/core/types.py
def latest_hash(self) -> bytes:
    """Compute the hash that the next entry should use as `prev_hash`.

    Returns:
        `entries[-1].compute_hash()` if the logbook has any entries;
        `EMPTY_PREV_HASH` (32 zero bytes) otherwise.
    """
    if not self.entries:
        return EMPTY_PREV_HASH
    return self.entries[-1].compute_hash()

append

append(kind: str, payload: dict[str, Any], signing_key: SigningKey, ts: datetime | None = None) -> LogEntry

Build, sign, and append a new entry.

Parameters:

Name Type Description Default
kind str

Non-empty event tag (e.g. "BIRTH", "SPAWN").

required
payload dict[str, Any]

JSON-serialisable dict; non-serialisable contents raise TypeIntegrityError at canonicalisation time.

required
signing_key SigningKey

The vacant's private Ed25519 key. The resulting signature will verify against the matching verify_key.

required
ts datetime | None

Optional explicit timestamp (coerced to UTC). Defaults to datetime.now(UTC) for fresh entries.

None

Returns:

Type Description
LogEntry

The newly-appended (signed) LogEntry. Mutates self.entries.

Source code in src/vacant/core/types.py
def append(
    self,
    kind: str,
    payload: dict[str, Any],
    signing_key: SigningKey,
    ts: datetime | None = None,
) -> LogEntry:
    """Build, sign, and append a new entry.

    Args:
        kind: Non-empty event tag (e.g. `"BIRTH"`, `"SPAWN"`).
        payload: JSON-serialisable dict; non-serialisable contents
            raise `TypeIntegrityError` at canonicalisation time.
        signing_key: The vacant's private Ed25519 key. The
            resulting `signature` will verify against the matching
            `verify_key`.
        ts: Optional explicit timestamp (coerced to UTC). Defaults
            to `datetime.now(UTC)` for fresh entries.

    Returns:
        The newly-appended (signed) `LogEntry`. Mutates `self.entries`.
    """
    entry_ts = (ts or datetime.now(UTC)).astimezone(UTC)
    prev = self.latest_hash()
    unsigned = LogEntry(
        kind=kind,
        ts=entry_ts,
        payload=payload,
        prev_hash=prev,
        signature=b"\x00",  # placeholder, replaced below
    )
    sig = sign(signing_key, unsigned.signing_payload())
    signed = unsigned.model_copy(update={"signature": sig})
    self.entries.append(signed)
    return signed

verify_chain

verify_chain(pubkey: VerifyKey) -> bool

Verify every signature and that the hash chain is intact.

Parameters:

Name Type Description Default
pubkey VerifyKey

The vacant's Ed25519 verify-key. Every entry must verify against this single key — keys do not rotate within a single logbook.

required

Returns:

Type Description
bool

True iff (1) every entries[i].prev_hash equals the

bool

previous entry's compute_hash() (or EMPTY_PREV_HASH for

bool

the genesis), AND (2) every entries[i].verify(pubkey)

bool

returns True. Never raises.

Source code in src/vacant/core/types.py
def verify_chain(self, pubkey: VerifyKey) -> bool:
    """Verify every signature and that the hash chain is intact.

    Args:
        pubkey: The vacant's Ed25519 verify-key. *Every* entry must
            verify against this single key — keys do not rotate
            within a single logbook.

    Returns:
        `True` iff (1) every `entries[i].prev_hash` equals the
        previous entry's `compute_hash()` (or `EMPTY_PREV_HASH` for
        the genesis), AND (2) every `entries[i].verify(pubkey)`
        returns `True`. Never raises.
    """
    expected_prev = EMPTY_PREV_HASH
    for entry in self.entries:
        if entry.prev_hash != expected_prev:
            return False
        if not entry.verify(pubkey):
            return False
        expected_prev = entry.compute_hash()
    return True

verify_chain_or_raise

verify_chain_or_raise(pubkey: VerifyKey) -> None

Strict variant of verify_chain that names the failing entry.

Parameters:

Name Type Description Default
pubkey VerifyKey

As for verify_chain.

required

Raises:

Type Description
HashChainError

On the first broken hash link or invalid signature. The message includes the entry index and, for hash mismatches, the expected vs actual hash.

Source code in src/vacant/core/types.py
def verify_chain_or_raise(self, pubkey: VerifyKey) -> None:
    """Strict variant of `verify_chain` that names the failing entry.

    Args:
        pubkey: As for `verify_chain`.

    Raises:
        HashChainError: On the first broken hash link or invalid
            signature. The message includes the entry index and,
            for hash mismatches, the expected vs actual hash.
    """
    expected_prev = EMPTY_PREV_HASH
    for i, entry in enumerate(self.entries):
        if entry.prev_hash != expected_prev:
            raise HashChainError(
                f"Logbook entry {i} prev_hash mismatch: "
                f"expected {expected_prev.hex()}, got {entry.prev_hash.hex()}"
            )
        if not entry.verify(pubkey):
            raise HashChainError(f"Logbook entry {i} signature invalid")
        expected_prev = entry.compute_hash()

SubstrateSpec

Bases: BaseModel

Multi-substrate declaration (THEORY_V5 §2).

Lists which substrates a vacant is willing to be invoked under (e.g. "anthropic:claude-sonnet-4-6", "openai:gpt-4o", "client-inherited") plus an opaque policy dict the dispatcher can use for substrate-specific gating. Substrate is a resource, not the identity — switching substrates does not change vacant_id.

Attributes:

Name Type Description
allowed_substrates list[str]

Non-empty substrate identifiers. Blank strings raise ValidationError.

policy dict[str, Any]

Free-form per-substrate config (rate limits, model overrides, …).

canonical_bytes

canonical_bytes() -> bytes

Stable byte encoding for hashing into a CapabilityCard.

Returns:

Type Description
bytes

Canonical JSON (sorted keys, no whitespace) of

bytes

{allowed_substrates, policy}. Used inside

bytes

CapabilityCard.signing_payload() so the substrate spec is

bytes

covered by the halo signature.

Source code in src/vacant/core/types.py
def canonical_bytes(self) -> bytes:
    """Stable byte encoding for hashing into a `CapabilityCard`.

    Returns:
        Canonical JSON (sorted keys, no whitespace) of
        `{allowed_substrates, policy}`. Used inside
        `CapabilityCard.signing_payload()` so the substrate spec is
        covered by the halo signature.
    """
    return _canonical_json(
        {
            "allowed_substrates": list(self.allowed_substrates),
            "policy": self.policy,
        }
    )

BehaviorBundle

Bases: BaseModel

System prompt + policy DSL + tool whitelist (Ricoeur character).

The bundle_hash is computed and self-validated at construction: pass an empty bundle_hash and it gets filled in; pass a non-empty one that doesn't match the content and TypeIntegrityError fires. Downstream code that wants to detect "did the behaviour change silently?" compares bundle_hash rather than serialising fields.

Attributes:

Name Type Description
system_prompt str

The prompt the substrate sees as its system message.

policy_dsl str

Optional policy DSL string (P5 future). Empty by default.

tool_whitelist list[str]

List of tool names the vacant is allowed to invoke. Hash is order-independent (sorted internally).

bundle_hash bytes

BLAKE2b-256 over a canonical JSON of the three fields. Auto-computed when left empty.

CapabilityCard

Bases: BaseModel

The halo — a vacant's self-published capability announcement.

Spec: THEORY_V5 §7.1. Each vacant carries its own halo; the "Registry" is an aggregation/index over halos and is never a routed-through component. Discovery hands a halo to the caller; the caller then dispatches directly to endpoint.

Attributes:

Name Type Description
vacant_id VacantId

The vacant the halo is about. Anyone can mint a card claiming to be vacant_id, but verify() will only return True if signature was produced by the matching private key.

capability_text str

Human-readable capability announcement (e.g. "I translate technical English to Traditional Chinese").

substrate_spec SubstrateSpec

Which substrates this vacant accepts. Covered by the signature.

halo_version int

Monotonic counter for halo revisions. Must be

= 1.

endpoint str | None

A2A endpoint URL for direct dispatch. None for LOCAL or not-yet-deployed vacants. Part of the signing payload so an attacker can't substitute the endpoint post-issuance (D009 §A).

signature bytes

Ed25519 signature over signing_payload().

signing_payload

signing_payload() -> bytes

Canonical bytes that get signed.

Concatenates pubkey | capability_text | substrate_spec | halo_version | endpoint, joined with 0x1f. signature itself is not included.

Returns:

Type Description
bytes

The exact byte-string fed to sign() / verify().

Source code in src/vacant/core/types.py
def signing_payload(self) -> bytes:
    """Canonical bytes that get signed.

    Concatenates `pubkey | capability_text | substrate_spec |
    halo_version | endpoint`, joined with `0x1f`. `signature`
    itself is not included.

    Returns:
        The exact byte-string fed to `sign()` / `verify()`.
    """
    parts = [
        self.vacant_id.pubkey_bytes,
        self.capability_text.encode("utf-8"),
        self.substrate_spec.canonical_bytes(),
        self.halo_version.to_bytes(8, "big"),
        (self.endpoint or "").encode("utf-8"),
    ]
    return b"\x1f".join(parts)

signed

signed(signing_key: SigningKey) -> CapabilityCard

Return a copy of this card with signature filled in.

Parameters:

Name Type Description Default
signing_key SigningKey

The vacant's private Ed25519 key. Must match the public key embedded in vacant_id or downstream verify() calls will fail.

required

Returns:

Type Description
CapabilityCard

A new CapabilityCard (the model is frozen) with a valid

CapabilityCard

signature.

Source code in src/vacant/core/types.py
def signed(self, signing_key: SigningKey) -> CapabilityCard:
    """Return a copy of this card with `signature` filled in.

    Args:
        signing_key: The vacant's private Ed25519 key. Must match
            the public key embedded in `vacant_id` or downstream
            `verify()` calls will fail.

    Returns:
        A new `CapabilityCard` (the model is frozen) with a valid
        `signature`.
    """
    sig = sign(signing_key, self.signing_payload())
    return self.model_copy(update={"signature": sig})

verify

verify() -> bool

Verify the signature against the embedded public key.

Returns:

Type Description
bool

True iff signature is non-empty AND validates as an

bool

Ed25519 signature over signing_payload() under

bool

vacant_id.verify_key(). Empty signatures (un-signed

bool

drafts) return False rather than raising.

Source code in src/vacant/core/types.py
def verify(self) -> bool:
    """Verify the signature against the embedded public key.

    Returns:
        `True` iff `signature` is non-empty AND validates as an
        Ed25519 signature over `signing_payload()` under
        `vacant_id.verify_key()`. Empty signatures (un-signed
        drafts) return `False` rather than raising.
    """
    if not self.signature:
        return False
    return verify(self.vacant_id.verify_key(), self.signing_payload(), self.signature)

VacantState

Bases: StrEnum

The 6 lifecycle states a vacant can be in (CLAUDE.md §LOCAL).

LOCAL is the default for newly-spawned vacants; it is fully runnable but never appears in the public registry index. The state machine in vacant.runtime.state_machine defines the legal transitions; predicates can_review / can_be_called / is_runnable derive admission rules from the state.

ResidentForm

Bases: BaseModel

The full 6-component vacant — identity through behaviour through halo.

ResidentForm is the in-memory representation of a vacant. P0 treats most of it as immutable; only runtime_state mutates as the lifecycle progresses. P1 onward also append to logbook via the explicit append() API.

Attributes:

Name Type Description
identity VacantId

VacantId (pubkey).

logbook Logbook

Append-only signed history.

behavior_bundle BehaviorBundle

System prompt + policy DSL + tool whitelist.

substrate_spec SubstrateSpec

Allowed substrate set.

runtime_state VacantState

One of VacantState. Defaults to LOCAL.

capability_card CapabilityCard | None

The signed halo. None until P4 publishes it.

parent_id VacantId | None

Lineage anchor (THEORY_V5 §4.3). None for root / Path-Zero vacants. Secondary parents (D4 lineage-merge) live in the BIRTH log entry payload — see D003 ADR.

verify_self

verify_self() -> bool

Cross-component integrity check.

Verifies (1) the logbook hash chain + every signature against identity.verify_key(), and (2) if a capability_card is attached, that its vacant_id matches identity and its signature verifies.

Returns:

Type Description
bool

True iff every checked component is intact. False if

bool

any signature, hash link, or identity mismatch is found.

bool

Never raises — callers that need exception semantics can

bool

wrap with Logbook.verify_chain_or_raise.

Source code in src/vacant/core/types.py
def verify_self(self) -> bool:
    """Cross-component integrity check.

    Verifies (1) the logbook hash chain + every signature against
    `identity.verify_key()`, and (2) if a `capability_card` is
    attached, that its `vacant_id` matches `identity` and its
    signature verifies.

    Returns:
        `True` iff every checked component is intact. `False` if
        any signature, hash link, or identity mismatch is found.
        Never raises — callers that need exception semantics can
        wrap with `Logbook.verify_chain_or_raise`.
    """
    pubkey = self.identity.verify_key()
    if not self.logbook.verify_chain(pubkey):
        return False
    card = self.capability_card
    if card is not None:
        if card.vacant_id != self.identity:
            return False
        if not card.verify():
            return False
    return True

constants

Numeric thresholds shared across components.

Every constant cites architecture/CONSTANTS.md plus the underlying spec section. Adding a new constant here REQUIRES a matching row in CONSTANTS.md (see architecture/decisions/D002_p0_bootstrap_constants.md for the P0 reconciliation between dispatch/P0_bootstrap.md and CONSTANTS.md).

HEARTBEAT_BASE_PERIOD_S module-attribute

HEARTBEAT_BASE_PERIOD_S: Final[int] = 60

Demo-default heartbeat period for an Active vacant. CONSTANTS.md §Lifecycle / P1 §D2.

HEARTBEAT_HIBERNATING_PERIOD_S module-attribute

HEARTBEAT_HIBERNATING_PERIOD_S: Final[int] = 86400

Heartbeat period while in HIBERNATING (one minimal attestation per 24h). CONSTANTS.md §Lifecycle / P1 §D6 line 73.

HEARTBEAT_DECAYED_PERIOD_S module-attribute

HEARTBEAT_DECAYED_PERIOD_S: Final[int] = HEARTBEAT_HIBERNATING_PERIOD_S

Back-compat alias for HEARTBEAT_HIBERNATING_PERIOD_S (kept since P0). The "(Sunk)" label in CONSTANTS.md was an artefact of an earlier pass; THEORY_V5 §4.2 splits SUNK out at 10 min. See D003.

HEARTBEAT_SUNK_LIVENESS_PERIOD_S module-attribute

HEARTBEAT_SUNK_LIVENESS_PERIOD_S: Final[int] = 600

Sunk-state custody-attestation heartbeat period (10 min). CONSTANTS.md §Lifecycle / THEORY_V5 §4.2 / §3 line 340.

IDEMPOTENCY_WINDOW_S module-attribute

IDEMPOTENCY_WINDOW_S: Final[int] = 86400

Window in which a (vacant_id, request_id) tuple must dedupe. CONSTANTS.md §Lifecycle / P1 §3.2.

STALE_AFTER_HIBERNATING_DAYS module-attribute

STALE_AFTER_HIBERNATING_DAYS: Final[int] = 30

Hibernating → Stale-flag transition threshold (no service >= 30 days). CONSTANTS.md §Lifecycle / D001.

ARCHIVED_AFTER_SUNK_DAYS module-attribute

ARCHIVED_AFTER_SUNK_DAYS: Final[int] = 180

Sunk → Archived transition threshold (180 days post-Sunk). CONSTANTS.md §Lifecycle / THEORY_V5 §3 line 318.

WARMUP_WINDOW_S module-attribute

WARMUP_WINDOW_S: Final[int] = 86400

Warmup-ceremony observation window after Stale → Active. CONSTANTS.md §Lifecycle / P1 §3.3.1 line 191.

WARMUP_REQUIRED_HEARTBEATS module-attribute

WARMUP_REQUIRED_HEARTBEATS: Final[int] = 5

Number of valid heartbeats required during warmup. CONSTANTS.md §Lifecycle / P1 §3.3.1 line 205.

STYLO_DRIFT_THRESHOLD module-attribute

STYLO_DRIFT_THRESHOLD: Final[float] = 3.5

Mahalanobis threshold on STYLO Vec16 used as drift trigger. CONSTANTS.md §Lifecycle / THEORY_V5 §3 line 152. Heuristic, not a p-value.

DEFAULT_HALO_VERSION module-attribute

DEFAULT_HALO_VERSION: Final[int] = 1

Initial value of CapabilityCard.halo_version when first published. dispatch/P0_bootstrap.md §4.

HASH_DIGEST_BYTES module-attribute

HASH_DIGEST_BYTES: Final[int] = 32

Output size of vacant.core.crypto.hash_blake2b in bytes.

ED25519_PUBLIC_KEY_BYTES module-attribute

ED25519_PUBLIC_KEY_BYTES: Final[int] = 32

Length of a raw Ed25519 public key (NaCl encoding).

ED25519_SIGNATURE_BYTES module-attribute

ED25519_SIGNATURE_BYTES: Final[int] = 64

Length of a raw Ed25519 signature.

PEER_ATTESTATION_FRESHNESS_WINDOW_DAYS module-attribute

PEER_ATTESTATION_FRESHNESS_WINDOW_DAYS: Final[int] = 30

Default validity window for a PeerAttestation (issued_at + 30d). CONSTANTS.md §Identity / P2 §4.

MIN_VOUCHERS_FOR_L3_PROMOTION module-attribute

MIN_VOUCHERS_FOR_L3_PROMOTION: Final[int] = 3

Default minimum number of valid peer attestations required to promote an L2Identity to L3Identity. CONSTANTS.md §Identity / P2 §2.

FEDERATION_ROOT_COUNT_MVP module-attribute

FEDERATION_ROOT_COUNT_MVP: Final[int] = 5

MVP attestation root set: 2-of-5. CONSTANTS.md §Identity / T4_attestation_bootstrap.

FEDERATION_ROOT_COUNT_TARGET module-attribute

FEDERATION_ROOT_COUNT_TARGET: Final[int] = 9

Long-term target attestation root set: 3-of-9. CONSTANTS.md §Identity / T4_attestation_bootstrap.

WASH_COST_FALSE_CLAIM_WEIGHT_DEFAULT module-attribute

WASH_COST_FALSE_CLAIM_WEIGHT_DEFAULT: Final[float] = 1.0

Multiplier on the per-history-entry forgery cost (P2 §3 / D004 §A). Tests vary this to verify cost increases monotonically with false-claim weight.

MERKLE_SNAPSHOT_INTERVAL_S module-attribute

MERKLE_SNAPSHOT_INTERVAL_S: Final[int] = 3600

Hourly Merkle epoch sealing cadence. CONSTANTS.md §Registry / P4 §3.

EVENT_LOG_MAX_PAGE_SIZE module-attribute

EVENT_LOG_MAX_PAGE_SIZE: Final[int] = 500

Read pagination caps for /v1/event_log/{vid} (P4 §3.2).

ANOMALY_SPAWN_PER_PARENT_HOUR module-attribute

ANOMALY_SPAWN_PER_PARENT_HOUR: Final[int] = 10

Rule-based anomaly thresholds (P4 §3.2 anomaly table). Surfaced as signals; the engine raises a triggered flag rather than auto-blocking.

REGISTRY_DB_DEFAULT_URL module-attribute

REGISTRY_DB_DEFAULT_URL: Final[str] = 'sqlite+aiosqlite:///:memory:'

Default SQLAlchemy async URL for in-memory testing. D006 §B.

REPUTATION_DIMS module-attribute

REPUTATION_DIMS: Final[tuple[str, ...]] = ('factual', 'logical', 'relevance', 'honesty', 'adoption')

Five reputation dimensions. P3 §3.2 / D008 §A.

BETA_BASE_PRIORS module-attribute

BETA_BASE_PRIORS: Final[dict[str, tuple[float, float]]] = {'factual': (1.0, 1.0), 'logical': (1.0, 1.0), 'relevance': (1.0, 1.0), 'honesty': (2.0, 1.0), 'adoption': (1.0, 3.0)}

Base Beta priors per dimension. P3 §3.2 / D008 §A.

CONSTANTS.md previously listed (1.5, 1.0) for F/L/R; that was the worked example's L1-attestation-applied value being mistakenly imported as the base. The L1 +0.5alpha boost is applied separately in cold_start.py so the bonus is auditable rather than baked in.

DIM_HALF_LIFE_DAYS module-attribute

DIM_HALF_LIFE_DAYS: Final[dict[str, int]] = {'factual': 90, 'logical': 180, 'relevance': 60, 'honesty': 30, 'adoption': 90}

Per-dimension half-life. P3 §3.2 / CONSTANTS.md §Reputation.

SOURCE_BASE_WEIGHTS module-attribute

SOURCE_BASE_WEIGHTS: Final[dict[str, float]] = {'ground_truth': 1.0, 'caller_review': 0.6, 'peer_review': 0.4, 'self_eval': 0.05, 'adoption_event': 0.3, 'redteam_probe': 0.8}

Per-source base weights. P3 §3.4 table 1 / CONSTANTS.md §Reputation.

SAME_MODEL_HEAVY_DISCOUNT module-attribute

SAME_MODEL_HEAVY_DISCOUNT: Final[float] = 0.25

Same-base-model peer review discount; >5 reviews/30d → heavier discount. P3 §3.4.1.

REVIEWER_CREDIBILITY_FLOOR module-attribute

REVIEWER_CREDIBILITY_FLOOR: Final[float] = 0.3

Reviewer credibility floor; even low-rep reviewers count for at least 0.3. P3 §3.4.2.

NOVELTY_DECAY_COEFFICIENT module-attribute

NOVELTY_DECAY_COEFFICIENT: Final[float] = 0.4

Per-repeat-review novelty decay. P3 §3.4.3.

COLLUSION_SEVERE_MULTIPLIER module-attribute

COLLUSION_SEVERE_MULTIPLIER: Final[float] = 0.1

Collusion graph thresholds. P3 §3.4.4.

UCB_CONSERVATIVE_C_EXPLORE module-attribute

UCB_CONSERVATIVE_C_EXPLORE: Final[float] = 0.1

UCB exploration coefficients. P3 §3.7.

N_SHOW_MIN_THRESHOLD module-attribute

N_SHOW_MIN_THRESHOLD: Final[int] = 10

Sample-size thresholds for stable scoring + UI display. P3 §3.7-§3.8.

COLD_START_FLOORS_BY_LEVEL module-attribute

COLD_START_FLOORS_BY_LEVEL: Final[dict[str, float]] = {'L0': 0.0, 'L1': 0.05, 'L2': 0.1, 'L3': 0.15}

Cold-start UCB floor by attestation level. P3 §3.7.

S_REF_USDC module-attribute

S_REF_USDC: Final[float] = 100.0

Reference stake amount (USDC equivalent) for stake-bonus normalisation. P3 §3.7.

L3_VOUCH_ALPHA_BOOST module-attribute

L3_VOUCH_ALPHA_BOOST: Final[float] = 0.3

Cold-start alpha boosts for L1 attestation (per F/L/R dim) and L3 vouch (per voucher, applied to H). P3 §3.8.

NETWORK_EXPLORATION_FLOOR module-attribute

NETWORK_EXPLORATION_FLOOR: Final[float] = 0.01

Minimum fraction of traffic reserved for new vacants. P3 §3.7 line 343.

DIMENSION_CORRELATION_ALERT_THRESHOLD module-attribute

DIMENSION_CORRELATION_ALERT_THRESHOLD: Final[float] = 0.6

Cross-dimension correlation alert (anti-Goodhart). P3 §3.6 line 290.

PORTABILITY_FACTOR_MAX_BONUS module-attribute

PORTABILITY_FACTOR_MAX_BONUS: Final[float] = 0.1

Maximum portability bonus added to UCB call_score for vacants serving multiple substrates. P3-derived; CONSTANTS.md §Reputation.

REVIEW_LIMIT_PER_TARGET_24H module-attribute

REVIEW_LIMIT_PER_TARGET_24H: Final[int] = 3

Per-target review rate limit. CONSTANTS.md §Review limits / P1 line 259. P3 aggregator enforces this in record_review to mitigate sniping (Padv-P3 finding D010 §1).

REVIEW_LIMIT_PER_DOMAIN_24H module-attribute

REVIEW_LIMIT_PER_DOMAIN_24H: Final[int] = 20

Per-reviewer per-capability-domain review rate limit. CONSTANTS.md §Review limits / P1 line 260.

PEER_REVIEW_BLOOM_TTL_S module-attribute

PEER_REVIEW_BLOOM_TTL_S: Final[int] = 86400

Per-(target, answer) Bloom-filter TTL on peer reviewer side. CONSTANTS.md §Review limits / P1 line 249.

CUMULATIVE_DRIFT_THRESHOLD_MULTIPLIER module-attribute

CUMULATIVE_DRIFT_THRESHOLD_MULTIPLIER: Final[float] = 1.5

Cumulative STYLO-distance window for the cumulative drift detector (Padv-P3 finding D010 §2). Sum of per-epoch drifts over a rolling window of CUMULATIVE_DRIFT_WINDOW_EPOCHS; trips when the sum exceeds STYLO_DRIFT_THRESHOLD * CUMULATIVE_DRIFT_THRESHOLD_MULTIPLIER.

IDLE_REVIEW_THRESHOLD_S module-attribute

IDLE_REVIEW_THRESHOLD_S: Final[int] = 3600

How long a vacant must be idle before being eligible to peer-review new vacants. Cold-start §3.6 hook.

SAME_CONTROLLER_DECLARED_STRENGTH module-attribute

SAME_CONTROLLER_DECLARED_STRENGTH: Final[float] = 1.0

Same-controller detection thresholds. T5 §3.3 / D008 §C.

SAME_SIGNAL_DISCOUNT_FLOOR module-attribute

SAME_SIGNAL_DISCOUNT_FLOOR: Final[float] = 0.1

Minimum residual weight a same-* signal can leave a review with.

Same- detection is cost-raising, not preventing* (CLAUDE.md §Load-bearing theory decisions / D015). Even a maximum-strength signal (strength=1.0) must not zero a reviewer's contribution — that would convert detection into a unilateral mute. This floor ensures discount_from_signals(...) returns at least SAME_SIGNAL_DISCOUNT_FLOOR when any signal fires, so suspected colluders still get downweighted but are never silenced. CONSTANTS.md §Reputation / D015.

A2A_VACANT_METADATA_KEY module-attribute

A2A_VACANT_METADATA_KEY: Final[str] = 'urn:vacant:v1'

A2A metadata key under which Vacant envelope fields are mounted (P6 §3.2).

CALL_TIMEOUT_DEFAULT_S module-attribute

CALL_TIMEOUT_DEFAULT_S: Final[int] = 60

Default A2A call timeout in seconds. P6 §3.4 server.json default.

REGISTRY_CACHE_TTL_S module-attribute

REGISTRY_CACHE_TTL_S: Final[int] = 300

Default registry cache TTL for MCP adapter. P6 §3.4.

GRADUATION_RATE_LIMIT_PER_PARENT_24H module-attribute

GRADUATION_RATE_LIMIT_PER_PARENT_24H: Final[int] = 3

Per-parent graduation rate limit over a 24h sliding window. CONSTANTS.md §Composite / D012 §A. Defaults to 3 to mirror P1's per-target review limit pattern; tunable per CompositeRuntime for demo orchestration.

GRADUATION_COLLUSION_THRESHOLD module-attribute

GRADUATION_COLLUSION_THRESHOLD: Final[float] = 0.6

Maximum tolerated same_* signal strength on the parent->child pair during graduation. Above this the graduation is blocked regardless of parent consent. CONSTANTS.md §Composite / D012 §B.

crypto

Pure crypto primitives used across the vacant stack.

Pure functions, no module-level mutable state, no global RNG. Ed25519 via PyNaCl is the canonical implementation (CLAUDE.md §Tech stack). BLAKE2b-256 is the canonical hash (matches LogEntry.prev_hash and CapabilityCard signing payloads).

keygen

keygen() -> tuple[SigningKey, VerifyKey]

Generate a fresh Ed25519 keypair using the OS CSPRNG.

Source code in src/vacant/core/crypto.py
def keygen() -> tuple[SigningKey, VerifyKey]:
    """Generate a fresh Ed25519 keypair using the OS CSPRNG."""
    sk = SigningKey.generate()
    return sk, sk.verify_key

sign

sign(key: SigningKey, msg: bytes) -> bytes

Detached Ed25519 signature over msg (returns 64 raw bytes).

Source code in src/vacant/core/crypto.py
def sign(key: SigningKey, msg: bytes) -> bytes:
    """Detached Ed25519 signature over `msg` (returns 64 raw bytes)."""
    if not isinstance(msg, bytes | bytearray):
        raise CryptoError("sign(): msg must be bytes")
    sig: bytes = key.sign(bytes(msg)).signature
    return sig

verify

verify(pubkey: VerifyKey, msg: bytes, sig: bytes) -> bool

True iff sig is a valid Ed25519 signature over msg for pubkey.

Returns False on any malformed input rather than raising; callers that want to surface tampering as an exception should raise SignatureVerificationError themselves on the False branch.

Source code in src/vacant/core/crypto.py
def verify(pubkey: VerifyKey, msg: bytes, sig: bytes) -> bool:
    """True iff `sig` is a valid Ed25519 signature over `msg` for `pubkey`.

    Returns False on any malformed input rather than raising; callers that
    want to surface tampering as an exception should raise
    `SignatureVerificationError` themselves on the False branch.
    """
    if len(sig) != ED25519_SIGNATURE_BYTES:
        return False
    try:
        pubkey.verify(bytes(msg), bytes(sig))
    except BadSignatureError:
        return False
    except Exception:
        return False
    return True

verify_or_raise

verify_or_raise(pubkey: VerifyKey, msg: bytes, sig: bytes) -> None

Like verify but raises SignatureVerificationError on failure.

Source code in src/vacant/core/crypto.py
def verify_or_raise(pubkey: VerifyKey, msg: bytes, sig: bytes) -> None:
    """Like `verify` but raises `SignatureVerificationError` on failure."""
    if not verify(pubkey, msg, sig):
        raise SignatureVerificationError("Ed25519 signature did not verify")

hash_blake2b

hash_blake2b(data: bytes) -> bytes

BLAKE2b digest truncated to HASH_DIGEST_BYTES (32 bytes).

Source code in src/vacant/core/crypto.py
def hash_blake2b(data: bytes) -> bytes:
    """BLAKE2b digest truncated to `HASH_DIGEST_BYTES` (32 bytes)."""
    return hashlib.blake2b(bytes(data), digest_size=HASH_DIGEST_BYTES).digest()

hex_encode

hex_encode(b: bytes) -> str

Lowercase hex encode.

Source code in src/vacant/core/crypto.py
def hex_encode(b: bytes) -> str:
    """Lowercase hex encode."""
    return bytes(b).hex()

hex_decode

hex_decode(s: str) -> bytes

Inverse of hex_encode. Raises CryptoError on malformed input.

Source code in src/vacant/core/crypto.py
def hex_decode(s: str) -> bytes:
    """Inverse of `hex_encode`. Raises `CryptoError` on malformed input."""
    try:
        return bytes.fromhex(s)
    except ValueError as exc:
        raise CryptoError(f"hex_decode(): not valid hex: {exc}") from exc

pubkey_from_bytes

pubkey_from_bytes(data: bytes) -> VerifyKey

Construct a VerifyKey from raw 32-byte public-key material.

Source code in src/vacant/core/crypto.py
def pubkey_from_bytes(data: bytes) -> VerifyKey:
    """Construct a `VerifyKey` from raw 32-byte public-key material."""
    if len(data) != ED25519_PUBLIC_KEY_BYTES:
        raise CryptoError(
            f"pubkey_from_bytes(): expected {ED25519_PUBLIC_KEY_BYTES} bytes, got {len(data)}"
        )
    return VerifyKey(bytes(data))

errors

Base error hierarchy for shared core types and crypto primitives.

CoreError

Bases: Exception

Base class for all errors raised from vacant.core.

CryptoError

Bases: CoreError

Crypto primitive failure (keygen / sign / verify / hash).

SignatureVerificationError

Bases: CryptoError

A signature did not verify against the supplied public key + message.

HashChainError

Bases: CoreError

A logbook entry's hash chain pointer did not match the expected previous hash.

TypeIntegrityError

Bases: CoreError

A core BaseModel failed an internal self-consistency check.