Tier 1 safety architecture¶
Status: inert framework; no production mutation is reachable or authorized.
Isolation and dependency direction¶
The active application graph remains:
Application -> ToolRegistry -> READ tools -> PfSenseClient
-> RestApiClient -> GET-only Transport
The new pfsense_mcp.tier1 package is a disconnected safety-domain library.
Application, factory, server, ToolRegistry, RestApiClient, and
PfSenseClient do not import it. It contains no MCP tool, endpoint path,
credential loading, transport call, payload sender, or executor. Its production
policy constant contains zero rules.
Recovery Contract¶
An authoritative record binds:
- random contract and operation identifiers;
- globally replay-resistant idempotency digest;
- exact WRITE capability, endpoint symbol, and uppercase HTTP method;
- protected canonical natural target identity and its public digest;
- target precondition fingerprint;
- protected normalized intent and deterministic intent digest;
- protected read-before-write snapshot and snapshot digest;
- rollback-plan version;
- creation, expiry, confirmation, state, and monotonic state version.
ProtectedArtifact is opaque ciphertext metadata. The framework deliberately
does not include a convenience plaintext codec. A future accepted encryption
provider must encrypt before persistence and keep its key outside the database.
HMAC authenticates the complete stored record and binds it to one store ID; it
does not replace encryption.
Canonical target identity¶
Each capability must define one unique natural identity projection. Unicode is normalized to NFC and serialized as sorted compact UTF-8 JSON. Floats and unsupported dynamic values are rejected. Numeric upstream IDs are locator hints only and are not authoritative.
Preparation requires exactly one natural-identity match. After atomic execution acquisition, the future capability adapter must immediately re-read by natural identity, require exactly one match, refresh any locator hint, and compare the stored target fingerprint. Missing, duplicate, moved, or drifted targets fail without sending.
Confirmation and intent¶
Owner approval and the agent invocation are separate facts:
- The owner confirmation authority binds its identity and algorithm, a nonce, contract/operation IDs, target digest/fingerprint, intent digest, issuance, and expiry into verified evidence and a confirmation digest.
- The execution request supplies only contract ID plus typed intent/target inputs. Authoritative state is loaded from the store.
- Capability, endpoint, method, target digest, and intent digest must match.
Confirmation is single-use and valid only while PREPARED and unexpired. The store has no permissive fallback: without a configured verifier it refuses. A future production provider must authenticate the owner outside prompt text; an LLM assertion is not confirmation.
State and atomic persistence¶
The closed state set is:
PREPARING -> PREPARED -> EXECUTING -> VERIFIED
| | | |
+-> FAILED +-> EXPIRED +-> FAILED +-> ROLLING_BACK
\-> RECONCILIATION |
^ +-> ROLLED_BACK
| +-> ROLLBACK_FAILED
+-----------------+
Only declared transitions are legal. The generic store cannot exit
RECONCILIATION; a future authenticated resolution service must implement the
declared manual-only conclusions. SQLite transactions use BEGIN IMMEDIATE, compare state and
version, update the HMAC-bound record, reserve/release the target, and append a
value-free state event in one commit. Duplicate contract, operation, or
idempotency identities fail. A unique target reservation prevents concurrent
execution/rollback for the same canonical target.
On restart, every HMAC/index-verified EXECUTING or ROLLING_BACK record moves to RECONCILIATION. It is never resent automatically. Whole-database rollback cannot be detected solely from the database; production activation therefore requires an owner-approved external anti-rollback anchor or equivalent durable monotonic evidence.
Mutation outcome contract¶
No mutation executor exists yet. A future capability-specific executor must:
- acquire PREPARED -> EXECUTING durably;
- re-read and verify target identity/fingerprint/config-history preconditions;
- transmit exactly one typed, bounded request to one allow-listed path/method;
- accept only capability-specific status and response shape;
- perform authoritative semantic read-after-write;
- enter VERIFIED only after read-back;
- enter FAILED only when no effect or failure is proven;
- enter RECONCILIATION for partial transmission, timeout after send, lost response, interrupted verification, or any ambiguous outcome;
- never automatically retry a mutation.
Rollback¶
Rollback is capability-specific and target-bound. VERIFIED -> ROLLING_BACK must reserve the same target, re-read it, detect unrelated changes, and apply only a deterministic inverse. Read-back must prove semantic restoration. Conflict or partial compensation cannot force a global restore; it enters ROLLBACK_FAILED or RECONCILIATION. Appliance configuration history may be captured as a protected artifact, but capture failure blocks mutation and a global revision must never overwrite unrelated changes automatically.
The rollback conflict check uses an integrity-protected expected post-forward
fingerprint sealed atomically with the successful VERIFIED transition. It
does not compare the live post-forward state to the original pre-forward
fingerprint, because the authorized mutation may intentionally change a field
covered by that complete fingerprint. The original fingerprint and protected
snapshot remain unchanged and define exact restoration; the sealed
post-forward fingerprint defines whether rollback may begin.
VERIFIED is not a reservation state: the canonical target reservation is
released the instant a contract reaches VERIFIED, so an unrelated contract
may acquire that same target before any rollback decision is made. This is a
deliberate, accepted design, not an oversight — fingerprint-drift detection on
the VERIFIED -> ROLLING_BACK re-acquisition is the safety net for this
window, not an extended reservation. Two consequences follow directly: (1) if
the target is unclaimed, ROLLING_BACK re-reserves it and proceeds normally;
(2) if another contract has since claimed the same target, re-acquisition
fails closed with a conflict refusal — never a corrupted or forced rollback —
and the original contract's rollback must wait or escalate to reconciliation.
A production capability with tight rollback-window requirements may need its
own target-scoped cooldown (see the rate/blast-radius policy) to reduce how
often this conflict is hit in practice; the underlying safety property does
not depend on that cooldown existing.
Policy and audit¶
Policy is an immutable set of exact (Capability, endpoint symbol, method)
rules. No prefix, wildcard, tool-name inference, or profile implication is
accepted. The checked-in production policy is empty.
Audit records may contain identifiers, capability, endpoint symbol, method, digests, states, sanitized failure/exception class, timestamps, and outcome. They never contain raw target identity, payload, intent, snapshot, response, credentials, exception messages, or tool arguments/results. State-transition metadata is committed atomically with contract state and is HMAC-authenticated as a contiguous state/version chain.
Future adapter containment¶
A capability adapter must never receive a general REST client or raw transport. One future central executor must own policy authorization, durable acquisition, the authoritative re-read, one exact send, outcome classification, verification, and state/audit persistence. An adapter may provide only typed projections and pure comparisons for one approved rule. It cannot choose a path or method at runtime, send directly, widen the field projection, perform bulk operations, or claim verification. Until that sealed boundary exists and is reviewed, adapter implementation remains blocked.
Remaining activation blockers¶
- capability-specific protected-artifact codec and key lifecycle;
- external anti-rollback/backup policy and secure retention/deletion;
- exact typed mutation and response models;
- exact endpoint/method/status/read-back semantics from disposable-lab OpenAPI;
- owner-authenticated confirmation service;
- target-scoped monotonic rate policy;
- capability-specific rollback and reconciliation runbook;
- production wiring, tool registration, profile activation, and live acceptance, each requiring explicit approval.
The decision options and candidate-specific abuse review are maintained in TIER1_ACTIVATION_DECISIONS.md.