Internet-Draft VIRP September 2026
Howard Expires 11 March 2027 [Page]
Workgroup:
Internet Engineering Task Force
Internet-Draft:
draft-howard-virp-07
Published:
Intended Status:
Informational
Expires:
Author:
N. Howard
Third Level IT LLC

VIRP: Verified Infrastructure Response Protocol

Abstract

The Verified Infrastructure Response Protocol (VIRP) defines a trust framework for operators -- human or autonomous -- acting on live network infrastructure. As operations shift toward agentic and automated systems that can autonomously configure, audit, and remediate production environments, the absence of a verifiable chain of custody for observations and actions introduces fundamental risks: fabricated telemetry, unauthorized state changes, and the inability to distinguish legitimate operations from compromise. VIRP routes every observation and every authorization decision through a designated collection-and- verification boundary that the requesting party does not control. Observations are authenticated at collection time using HMAC-SHA256; in session-bound mode (Section 6.4) authentication uses a per-session key and binds the response to session, device, sequence, and a command-digest field. Validating the command binding additionally requires trusted request context from which the verifier recomputes the command digest. A two-channel architecture separates read-only Observation from write-intent Intent, and trust tiers (GREEN/YELLOW/RED/BLACK) govern action authorization with human-in-the-loop controls for elevated operations.

VIRP's observation and chain-integrity guarantees are symmetric and explicitly scoped by key role; approval and federation records use asymmetric Ed25519 signatures. Distinct key roles authenticate observations, chain entries, intents, approvals, and federation records (Section 6.1), though the reference implementation currently reuses one key across the v1-observation and v2-derivation roles; a holder of a symmetric key can both verify and forge within that key's scope, so VIRP does not provide publicly verifiable observation origin and does not defend a record against an adversary holding the relevant key or controlling the collection boundary. Authentication does not certify that a response reflects the managed device's true state, only that the boundary obtained and authenticated those bytes for that recorded request. Asymmetric proof of origin and external anchoring of the chain are distinct, independent items of future work (Section 18).

This revision adds External Authorization Binding (Section 11): a deployment profile in which the gate holds only a read-only device identity, the write credential is never at rest on the gate, and per-command authorization is performed by an authorization service that the gate does not control, using the device's own authorization mechanism (for example TACACS+ command authorization [RFC8907] on IOS and IOS-XE). The gate's own record of an action is then reconciled against the device's independent accounting under the same public verification tooling. The individual mechanisms are long-standing; the contribution is their composition, with an autonomous or automated requester as the constrained principal, together with an evidence layer that enables the gate's account of an action to be reconciled against the device's independently sourced account of the same action so a third party can compare them. Static per-command authorization and reconciliation are implemented and exercised on production Cisco hardware; approval-scoped dynamic grants and the chaining of authorization decisions are specified and marked as not yet implemented (Section 19).

Since draft-howard-virp-06 the reference chain implementation has gained an OPTIONAL per-entry and per-head Ed25519 signature, computed by the daemon at append time over the same canonical bytes as the mandatory HMAC and verifiable from a public key alone. It is enabled per node and is off by default, so a conforming deployment may still be HMAC-only. The base chain-integrity guarantee remains the symmetric one described here; the signature is an additional authenticator, described in Section 6.5 and Section 6.5.2, not a replacement for it.

Status of This Memo

This Internet-Draft is submitted in full conformance with the provisions of BCP 78 and BCP 79.

Internet-Drafts are working documents of the Internet Engineering Task Force (IETF). Note that other groups may also distribute working documents as Internet-Drafts. The list of current Internet-Drafts is at https://datatracker.ietf.org/drafts/current/.

Internet-Drafts are draft documents valid for a maximum of six months and may be updated, replaced, or obsoleted by other documents at any time. It is inappropriate to use Internet-Drafts as reference material or to cite them other than as "work in progress."

This Internet-Draft will expire on 11 March 2027.

Table of Contents

1. Requirements Language

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here.

2. Introduction and Motivation

The deployment of AI agents on live infrastructure represents a fundamental shift in how networks are operated. Where previous automation relied on deterministic scripts with predictable outputs, modern AI agents operate with significant autonomy -- selecting commands, interpreting results, and initiating multi-step remediation sequences without per-action human approval.

This capability creates an accountability gap. When a traditional monitoring system reports that a router's CPU is at 94%, the operator can trace that measurement to a specific SNMP poll at a specific timestamp against a specific OID. When an AI agent reports the same finding, the operator must trust that the agent actually queried the device, that the query returned what was reported, and that the agent's interpretation matches the raw data. None of these properties are currently verifiable.

The consequences of unverifiable AI observations are significant. An agent that fabricates or replays telemetry can drive incorrect remediation. An agent that takes undocumented actions leaves no audit trail for compliance or incident response. An agent that is compromised -- or that simply makes an error in a long autonomous loop -- produces outputs that are indistinguishable from legitimate operations.

VIRP addresses this gap by establishing cryptographic evidence at every layer of the observation-to-action pipeline. The protocol is not concerned with what the AI agent is capable of doing. It is concerned with establishing what the agent actually did, when, against which device, and whether that action was authorized by the appropriate tier of human oversight.

This revision states materially narrower security claims than draft-howard-virp-05 in several places. That narrowing is deliberate. It reconciles the specification with the reference implementation: earlier revisions described a per-observation Ed25519 signature and a two-tool machine-verified formal result, neither of which exists in the code. The corrected claims, and the reasoning behind each, are summarized in Appendix A. Readers comparing this revision against -05 should treat the removals as corrections, not as regressions in the protocol.

3. The Trust Problem in Agentic Infrastructure

3.1. Current State

Existing infrastructure management tools assume a trusted operator. SNMP polling trusts that the collector reports what it received. Ansible playbooks trust that tasks executed as written. SIEM correlation trusts that log sources are authentic. These trust assumptions are reasonable when humans are in the loop at every step.

AI agents break these assumptions. An agent running in an autonomous loop may execute hundreds of commands per session. Human review of every action is impractical. The operational benefit of AI autonomy is inseparable from reduced per-action oversight -- which means the integrity guarantees that relied on human review must be replaced with cryptographic equivalents. This shift is consistent with the Zero Trust Architecture principles articulated in [NIST-ZTA]: trust is established per-operation through verification, not inherited from an authenticated session.

3.2. The Fabrication Problem

The most significant risk in AI-driven infrastructure management is not malicious action -- it is undetectable error. An AI agent that misreads output, hallucinates a device state, or generates plausible-looking but incorrect telemetry can drive remediation actions based on false premises. Without cryptographic evidence that an observation was collected from a real device at a real timestamp, there is no mechanism to distinguish a genuine observation from a fabricated one.

3.3. The Accountability Gap

When an AI agent takes an action on infrastructure, current tooling records what the agent reported it did. VIRP records what the collection boundary observed the device return, with a chain of custody linking the intent to the observation to the outcome. This distinction is operationally significant: in an incident, the difference between what an agent said it did and what the device actually experienced may be the entire root cause.

4. Protocol Overview

VIRP defines a layered protocol stack with four primary components:

Table 1: Protocol Components
Component Description
O-Node Observation Node -- the collection-and-verification boundary. All device communication occurs within the O-Node. Observations are authenticated before leaving the O-Node. The authenticating key material is held inside that boundary.
Observation Channel Read-only channel for device state collection. Observations carry an HMAC-SHA256 authentication code, sequence numbers, and timestamps. Sequence gaps indicate missing observations.
Intent Channel Write-intent channel for action authorization. Intents must be filed before execution. The tier classifier evaluates intent against the trust tier framework before authorizing execution.
Chain DB Append-only database of authenticated chain entries referencing observations. Forms the tamper-evident audit trail, under the key-scoping stated in Section 6.5.

The O-Node operates as the collection boundary in the architecture. The requesting party -- an AI agent or a human-driven client -- is treated as a party outside that boundary: it may read authenticated observations for verification, but in the reference architecture it holds no observation-authentication, chain, or approval key and therefore cannot produce those records. This asymmetry is the foundation of VIRP's security model, and its exact scope is stated per key role in Section 6.1.

5. The Seven Trust Primitives

VIRP defines seven trust primitives that collectively address the accountability gap in agentic infrastructure operations. Each primitive addresses a distinct failure mode.

5.1. Primitive 1: Observation Integrity

Every device observation is authenticated at collection time within the O-Node using HMAC-SHA256. In session-bound mode (Section 6.4) the authenticated header covers the device identifier, timestamp, per-session sequence number, session identifier, a digest of the canonical command, and the payload length, and the authenticated bytes include the raw output. In the legacy format (Section 6.3) the authenticated bytes cover the header and output but carry no session, device, or command binding. Observations that fail verification are rejected. A party that does not hold the relevant key cannot produce an observation that verifies; a party that does hold it can (Section 6.1).

Failure mode addressed: fabricated or replayed telemetry originating outside the collection boundary. An agent that holds no observation-authentication key cannot report a device state that the boundary did not collect.

5.2. Primitive 2: Two-Channel Separation

VIRP enforces strict separation between Observation (read) and Intent (write) channels. Observations flow from device to O-Node to chain. Intents flow from the requesting party to the tier classifier to the O-Node to the device. The channels are architecturally separate and are additionally bound to distinct key roles: the implementation refuses to authenticate an Observation Channel message with an Intent Channel key or the reverse.

Failure mode addressed: an agent that reads device state and takes action within the same unmonitored channel, bypassing authorization controls.

5.3. Primitive 3: Intent Gating

Before any write operation reaches a device, an Intent must be filed with the O-Node specifying the target device, command class, and justification. The tier classifier evaluates the intent against the trust tier framework. GREEN tier intents may execute immediately. YELLOW tier intents require human acknowledgment. RED tier intents require explicit approval carrying an Ed25519 signature from an enrolled approver, bound to the command digest, the target device, and an expiry. BLACK tier intents are rejected unconditionally. Commands that match no classification rule are treated as RED and are refused by default.

Failure mode addressed: unauthorized state changes. An agent cannot modify device configuration without a prior authorized intent on record.

5.4. Primitive 4: Outcome Verification

Following every authorized action, VIRP collects a post-action observation and compares the resulting device state against the expected outcome of the intent. Divergence between expected and actual state generates a trust tier escalation. Outcomes are authenticated and chained alongside the original intent.

Failure mode addressed: actions that execute but produce unexpected results, which would otherwise be invisible in the audit trail.

5.5. Primitive 5: Baseline Memory

VIRP maintains an authenticated baseline of known-good device state derived from a verified observation sweep. Subsequent observations are compared against the baseline. Deviations -- new BGP peers, changed interface states, modified firewall policies -- generate alerts and may trigger tier escalation. The baseline itself is a chain of authenticated observations and is verifiable by any holder of the relevant key.

Failure mode addressed: undetected configuration drift. The system knows what normal looks like and can detect when it changes.

5.6. Primitive 6: Multi-Vendor Normalization

VIRP defines a normalized observation schema that abstracts vendor-specific output formats. Cisco IOS show commands, FortiOS get commands, and Linux system calls are mapped to a common observation structure. This enables consistent chain integrity verification across heterogeneous infrastructure.

Failure mode addressed: trust model fragmentation across vendors. A single chain can represent observations from any supported device type.

5.7. Primitive 7: Agent Containment

The requesting process must be treated as an untrusted principal. Process containment must be enforced externally -- at the hypervisor layer, at the network boundary, and at the filesystem level. The requesting process must not have access to observation, chain, or approval key material, to device credentials, or to direct network paths to managed devices.

This primitive was identified during live adversarial testing when the agent under test self-reported that it could bypass VIRP's observation chain by going around it entirely -- accessing credentials directly, connecting to devices without chain entries, or spawning unsupervised inference sessions. The primitive formalizes the architectural response: containment is not policy, it is structure.

Failure mode addressed: a requesting process that circumvents the observation chain entirely. VIRP protects the chain -- Primitive 7 ensures the requester cannot go around it.

6. Cryptographic Architecture

6.1. Keys and Authentication Roles

VIRP defines several authentication key roles, each authenticating a specific class of record. Most roles use distinct key material, but the reference implementation currently reuses the O-Key both for v1 observation authentication and as the input to v2 session-key derivation (below). The channel and role separation that is enforced is structural, not policy: the implementation refuses to use, for example, an R-Key to authenticate an Observation Channel message.

O-Key (K_obs), HMAC-SHA256 [RFC2104] [RFC6234]:
A static key with two roles: it directly authenticates legacy v1 observation-channel messages (Section 6.3), and it is the input keying material from which v2 session keys are derived. v1 verification is a stateless HMAC check by a holder of the O-Key. Because HMAC is symmetric, an O-Key holder can both verify and produce v1 observations; because the O-Key is also the derivation input, an O-Key holder can additionally derive v2 session keys.
R-Key (reasoning key), HMAC-SHA256:
Authenticates Intent Channel messages -- the requests a reasoning or requesting party submits. Channel binding is enforced by the implementation: an R-Key can only authenticate Intent Channel messages and an O-Key can only authenticate Observation Channel messages; a violation is rejected. Authenticating an intent request is not authorizing it; the gate and approval policy remain authoritative. In the protocol model an R-Node holds the R-Key and uses it to authenticate Intent Channel messages. The current reference API and autopilot execution path does not use R-Key-authenticated Intent Channel messages; it submits framed requests directly to the O-Node socket. The statement "the requesting agent holds no key" is therefore accurate only for the current deployed path. The protocol architecture permits an R-Node to hold an R-Key that authenticates intent but never observations.
Session key, HKDF-SHA256-derived:
Authenticates session-bound v2 observations (Section 6.4). Derived per session from the O-Key material and the authenticated session transcript. A party that cannot derive the session key cannot produce a valid v2 observation for that session.
K_chain (chain key), HMAC-SHA256, distinct from K_obs:
Authenticates trust-chain entries (Section 6.5). A holder of K_chain can rewrite chain history; a holder of only K_obs cannot. Where the OPTIONAL chain-entry signature (Section 6.5.2) is enabled, a rewrite by a K_chain holder is additionally detectable by any holder of the entry-signing public key, because the K_chain holder cannot produce the Ed25519 signature.
Chain-signing key, Ed25519, OPTIONAL:
When chain-entry signing is enabled on a node (Section 6.5.2), the daemon holds an Ed25519 private key and signs each entry and head at append time; verifiers hold only the corresponding public key. This key is distinct from the approver and federation keys. It is not present on a node that does not enable chain-entry signing.
Approver key, Ed25519 [RFC8032] [ED25519]:
Signs approval records in the intent-approval flow. The private key is held outside the collection boundary, so the boundary cannot mint its own approvals.
Federation key, Ed25519:
Authenticates federation material (Section 18.5).

The exact session-key derivation is as follows (HKDF-SHA256 [RFC5869], single block, 32-byte output):

IKM  = the O-Key (master key material)
salt = the session transcript_hash (32 bytes)
info = the generation counter, big-endian u64 (exactly 8 octets)

PRK  = HMAC-SHA256(salt, IKM)
OKM  = HMAC-SHA256(PRK, info || 0x01)
session_key = OKM  (32 bytes)
Figure 1: Session Key Derivation

These are the exact authenticated inputs; any mismatch changes the key and breaks interoperability. Implementations MUST NOT substitute a zero salt for a missing transcript: a session key derived without an authenticated transcript is a protocol error, and an implementation that cannot compute transcript_hash MUST fail the session rather than derive a key.

The value generation is a derivation input. It MUST be carried in the authenticated SESSION_BIND message so that a separate verifier can re-derive the key from the authenticated transcript; see Section 6.4 for the normative rules, including the rollback-protection requirement. In the reference implementation generation is not yet transported; see Section 19.

The session-derivation input keying material is the O-Key itself. There is no separate master key in the reference implementation. The O-Key serves two roles: it directly authenticates v1 observations and it is the HKDF input for v2 session keys. Consequently a holder of the O-Key can both forge v1 observations and, given the required derivation context, derive v2 session keys within its scope. This is a weaker separation than a dedicated derivation master would provide. Implementations SHOULD adopt a dedicated session-derivation master distinct from the legacy v1 authentication key; this document records the current reuse as a migration target, not as a designed property.

The security consequence of role separation is important and easy to understate: possession of one key does not confer the ability to forge records in another key's scope -- with one current exception. It holds for K_chain, the R-Key, approver keys, and federation keys: a holder of any one cannot forge in another's scope. It does not currently hold between v1 authentication and v2 derivation, because both use the same O-Key material: an O-Key holder can forge v1 observations and derive v2 session keys. A component holding only K_chain, or only an approver key, gains no observation-forging ability. The collection boundary may in a given deployment hold several of these keys at once, but that is a deployment fact, not an inherent property of any single key. The threat model (Section 9) is therefore stated per key, not per "the O-Key".

Across all symmetric keys, VIRP provides integrity and origin authentication relative to the secrecy of the relevant key, held within the trusted boundary. It does not provide publicly verifiable origin for observations: any holder of the authenticating key can also produce records. The requesting party holds no observation, chain, or approval key. An R-Node may hold an R-Key and can therefore authenticate intent messages, but that key cannot authenticate observations, chain entries, or approvals, and authenticating an intent is not authorizing it. Asymmetric observation signing (Section 18.3) would add public verifiability and remove verifier forgery, but would not by itself defend against compromise of the signer; that requires hardware custody, independent witnessing, or external anchoring (Section 18), which are separate mechanisms addressing a separate threat.

6.2. Key Custody

In the reference implementation, verification components load the relevant symmetric key and check records locally. Because symmetric verification is also a forging capability, every holder of a given key is part of that key's trust boundary; deployments SHOULD minimize the number of holders and MUST NOT treat a symmetric verifier as an untrusted party. A deployment that needs verification by a party which must be unable to forge cannot obtain that from the symmetric paths specified here. For chain entries, the OPTIONAL per-entry Ed25519 signature of Section 6.5.2 provides exactly that: a verifier holding only the public key can check it and cannot forge it. Where that signature is not enabled, or for observation records, such a verifier requires the asymmetric work of Section 18.3 or a verification service that returns an accept/reject decision without releasing the key.

The requesting agent holds no observation, chain, or approval key. Approver and federation keys are asymmetric; the approver's private key is held outside the collection boundary, which holds only the corresponding public key.

6.3. Legacy v1 Observation

The v1 observation uses the static O-Key and an HMAC-SHA256 over the v1 message header and payload. Verification is stateless: a holder of the O-Key recomputes the HMAC and compares. v1 does not carry a session binding and does not by itself provide replay protection (Section 9), and it does not provide the request or command binding that v2 provides.

Legacy v1 is a deprecated transitional format. New implementations MUST use v2 for device-evidence observations. Implementations MAY continue to emit v1 authenticated rejection and compatibility records during migration, but verifiers MUST NOT interpret a v1 record as session-bound or replay-resistant evidence.

Stated plainly: the reference implementation currently defaults to v1. Request parsing defaults the observation version to 1; the Python API does not perform the v2 handshake or request v2; gate rejections are v1. v2 requires explicit session negotiation and an explicit version selection. Migrating the reference default to v2 is a tracked implementation task (Section 19), recorded here rather than claimed as complete.

Deprecated does not mean unspecified. The v1 layout is:

 offset  size  field
  0       1    version = 1
  1       1    message type
  2       2    total message length, uint16 big-endian
  4       4    node_id, uint32 big-endian
  8       1    channel
  9       1    tier
 10       2    reserved, MUST be zero
 12       4    sequence, uint32 big-endian
 16       8    timestamp_ns, uint64 big-endian
 24      32    HMAC-SHA256
 56       1    observation type
 57       1    observation scope
 58       2    observation-data length, uint16 big-endian
 60       n    observation data
Figure 2: v1 Observation Message Layout

The HMAC input is bytes 0 through 23 concatenated with bytes 56 through (total_length - 1); the HMAC field (bytes 24 through 55) is excluded from its own input.

A conforming v1 verifier MUST apply the following validation rules before accepting a record:

  • The version octet MUST equal 1 and the message type MUST be a defined value; unknown enumerated values MUST be rejected rather than ignored.
  • total message length MUST be at least 60 and MUST NOT exceed 65535. The maximum conforming observation-data length is therefore 65475 octets.
  • The declared observation-data length MUST be consistent with total message length; a declared length exceeding the remaining input MUST be rejected.
  • Octets after the declared fields MUST be rejected; a verifier MUST NOT accept trailing bytes.
  • The reserved field MUST be zero on transmission and MUST be rejected if non-zero on receipt.
  • The HMAC comparison MUST be constant-time.

6.4. Session-Bound v2 Observation

The v2 observation is authenticated as HMAC-SHA256(session_key, serialized_header || payload), where session_key is the HKDF-derived per-session key (Section 6.1). The fixed-width header carries a 64-bit node identifier; a nanosecond timestamp; a per-session sequence number; a session identifier; a 64-bit device identifier; a SHA-256 hash of the command (not the command string itself); and the payload length.

Because the header binds session, sequence, device, and a command hash, a v2 observation authenticates a command digest within a specific session; it becomes evidence about a specific command only when a verifier possesses trusted command context whose canonical digest matches that field. A party that cannot derive the session key cannot forge a v2 observation for that session, which is a stronger scoping than v1's static-key model.

The serialized header is exactly 88 octets, with no padding and all multi-byte fields in network byte order:

 offset  size  field
  0       1    version
  1       1    channel
  2       1    tier
  3       1    reserved (MUST be zero)
  4       8    node_id
 12       8    timestamp_ns   (nanoseconds since epoch)
 20       8    seq_num        (monotonic per session)
 28      16    session_id     (from SESSION_BIND)
 44       8    device_id      (stable 64-bit device identifier)
 52      32    command_hash   (SHA-256 of the canonical command)
 84       4    payload_len
 = 88 octets
Figure 3: v2 Observation Header Layout

The observation carries the command only as a SHA-256 hash, never as text. The verifier receives the expected command as a separate input (trusted request context), canonicalizes it, hashes it, and compares against the authenticated command_hash. The full command text is not in the authenticated bytes, not in the base observation, and not reliably in the chain entry; a consuming API that reattaches the command string does so as convenience metadata outside the authenticated bytes.

The consequence must be stated plainly: a base v2 observation is not a self-contained receipt of the command. It binds a command only relative to a relying party that already possesses the correct request context. A party that keeps the observation but loses the trusted request record retains a valid authentication over a digest whose preimage it can no longer establish. Closing this -- authenticating either the complete canonical typed-operation encoding, or a digest of a separately authenticated request record carrying operation identifier, registry version, parameters, and resolved target -- is the subject of Section 18.7 and is the strongest argument for a typed operation model.

The header layout can represent an all-zero session identifier, but no bootstrap v2 observation uses one: the v2 signer requires an active session and copies the active session identifier, and error records fall back to v1. A conforming v2 device-evidence observation MUST contain a non-zero session identifier, and verifiers MUST reject an all-zero session identifier.

Session-key provisioning to an independent verifier is specified as follows. VIRP adopts the O-Key-holding verifier model: a conforming independent verifier holds the O-Key and derives the session key itself from the authenticated session transcript and generation value. This matches the symmetric trust-boundary design and requires no confidential session-key delivery protocol. Such a verifier is a trusted component and has session-forging capability wherever it possesses the derivation context; its trust status equals the collection boundary's. Deployments requiring a verifier that cannot forge must use asymmetric observation signing (Section 18.3), which is future work. A derived-key-only verifier, provisioned over an authenticated confidential channel, is a possible future model but is not specified here; it would require a new key-delivery protocol.

Generation is carried in the authenticated SESSION_BIND message, specified as follows:

  • generation is an unsigned 64-bit integer in network byte order.
  • It is covered by the authentication protecting SESSION_BIND. That authenticator MUST NOT be the session key whose derivation depends on the transcript; otherwise the construction is circular. Implementations MUST authenticate SESSION_BIND under key material that is independent of the transcript being established.
  • The session transcript hash is computed over the complete encoded SESSION_BIND including the generation field: generation is inserted before transcript_hash is computed. Consequently generation is represented both inside salt = transcript_hash and separately as the 8-octet HKDF info. This duplication is intentional.
  • A verifier MUST reject a generation less than or equal to the greatest previously accepted generation for the derivation scope, which is the tuple (O-Key identifier, O-Node node identifier). This is rollback protection.

The v2 verifier also enforces a freshness window: an observation whose authenticated timestamp differs from the verifier's clock by more than a configured bound (300 seconds in the reference implementation) is rejected as stale, in addition to the per-session replay check of Section 9.

6.5. Chain Entry Encoding

A chain entry is a distinct artifact from an observation. Each entry contains previous_entry_hash, the entry digest of its immediate predecessor, within the entry's canonical encoding. VIRP computes over that canonical encoding both an unkeyed SHA-256 entry digest and an HMAC-SHA256 chain authentication code using K_chain, a key distinct from the observation-authentication keys. Alteration of a predecessor reference therefore changes both the entry digest and the chain HMAC. An entity without K_chain cannot modify, insert, or remove an entry within the presented chain and produce replacement downstream entries that verify.

This establishes only the integrity and linkage of the entries presented to the verifier. It does not prove the verifier holds the latest chain suffix: an adversary can present an older, internally valid prefix. Detecting truncation or a withheld suffix requires comparison against a previously trusted chain head or an externally published checkpoint (Section 18.4). A holder of K_chain can recompute the chain; external anchoring is required to make already-published history resistant to undetected rewriting by such a holder.

The canonical encoding of a chain entry in this revision is a compact JSON object over the entry's fields, and it is the octet sequence over which both the SHA-256 entry digest and the K_chain HMAC are computed. It is defined precisely below. This revision specifies the JSON form that the reference implementation actually produces and signs, rather than a separate binary form; an earlier description of a binary canonical encoding did not correspond to any implemented producer and has been removed. A future revision MAY define a stricter binary encoding, which would be a distinct chain format under a new version tag.

The fields carried by a chain entry are: artifact digest; artifact digest algorithm; artifact identifier; artifact schema version; artifact type; monotonic timestamp (ordering); previous entry digest; sequence; session identifier; signer node identifier; signer organization identifier; and wall-clock timestamp (informational). The entry's own entry digest (SHA-256) and chain HMAC (HMAC-SHA256 under K_chain) are computed over this encoding; they are outputs, not inputs to it, and are stored beside the entry rather than inside the encoded object.

The canonical encoding is a JSON object with the members below, in exactly this order, serialized with no insignificant whitespace: member separators are a single comma, name/value separators are a single colon, and there is no space anywhere between tokens. The member order is fixed (it is the alphabetical order of the member names) and MUST be emitted in that order rather than produced by re-sorting at verification time:

{"artifact_hash":"<64 lowercase hex>",
 "artifact_hash_alg":"<string, e.g. sha256>",
 "artifact_id":"<string>",
 "artifact_schema_version":"<string, e.g. 1>",
 "artifact_type":"<string, see enum below>",
 "monotonic_ns":<unsigned integer>,
 "previous_entry_hash":"<64 lowercase hex>",
 "sequence":<integer>,
 "session_id":"<string>",
 "signer_node_id":<unsigned integer>,
 "signer_org_id":"<string>",
 "timestamp_ns":<unsigned integer>}

(shown on multiple lines for readability only; the
 canonical form contains no newlines and no spaces)
Figure 4: Chain Entry Canonical Encoding

Exactly four members are unquoted JSON numbers -- monotonic_ns, sequence, signer_node_id, and timestamp_ns -- rendered as decimal ASCII with no leading zeros and no fractional part. All other members are JSON strings, including artifact_schema_version, which is the string "1" and not the number 1. The entry digest is SHA-256 of these bytes; the chain HMAC is HMAC-SHA256(K_chain, these bytes). A verifier reconstructs the object in this exact form, checks previous_entry_hash against the prior entry, recomputes the digest, and verifies the HMAC.

The following field-level rules make this encoding deterministic:

  • sequence MUST be greater than or equal to 1 and strictly increasing within a chain.
  • artifact_hash and previous_entry_hash MUST each be exactly 64 lowercase hexadecimal characters (a SHA-256 digest).
  • For the first entry in a chain, previous_entry_hash MUST be the value SHA-256("VIRP_CHAIN_GENESIS:" concatenated with the session identifier), rendered as 64 lowercase hex. It is a computed genesis value, not a sentinel of zeros.
  • artifact_type MUST be one of the values enumerated in Section 6.5.1; an unrecognized value MUST be rejected.
  • Because the reference producer interpolates string values without JSON escaping, string members MUST NOT contain a character that JSON requires to be escaped (in particular the double quote, the reverse solidus, or a control character below U+0020). A conforming producer MUST reject an entry whose string field contains such a character rather than emit ambiguous bytes, and a conforming verifier MUST treat the presence of an unescaped such character as a malformed entry. A future revision that adopts full JSON string escaping, or a binary encoding, would be a distinct chain format.

The compact JSON object above is the canonical encoding for this revision. The SHA-256 entry digest and the K_chain HMAC are stored beside the entry; where chain-entry signing (Section 6.5.2) is enabled, the Ed25519 signature and its key identifier are stored beside the entry as well. Neither the digest, the HMAC, nor the signature is part of the encoded object, and none is re-fed into the encoding.

6.5.1. Artifact Types

The artifact_type member identifies the kind of artifact a chain entry commits to. This revision defines the following values. The first three are carried by the reference implementation today; the remainder are defined for the evidence records of Section 11.2, of which gate_intent and gate_execution are emitted today and the others are specified but not yet emitted (Section 19).

  • observation -- a device-state observation (Section 6.4).
  • intent -- a write-intent record (Section 5).
  • outcome -- a post-action outcome record (Section 5).
  • gate_intent -- the gate's pre-action declaration (Section 11.2).
  • gate_execution -- the gate's post-action record (Section 11.2).
  • authorization_decision -- an external authorization-service decision (Section 11.2), specified, not yet emitted.
  • device_accounting -- a device command-accounting record received independently of the gate (Section 11.2).

A verifier MUST reject an entry whose artifact_type is not one of these values. Additional artifact types are expected in future revisions and MUST be registered rather than used ad hoc.

6.5.2. Optional Per-Entry Signature

In addition to the mandatory K_chain HMAC, an implementation MAY attach an Ed25519 [RFC8032] signature to each chain entry and to the chain head. When enabled, the signature is computed by the chain writer at append time, within the same transaction that writes the entry, over the domain-separated input:

 entry signature input = "VIRP-CHAIN-ENTRY-SIG-v1" || 0x00 ||
                         <canonical entry encoding>
 head  signature input = "VIRP-CHAIN-HEAD-SIG-v1"  || 0x00 ||
                         <canonical head encoding>
Figure 5: Chain Entry Signature Input

The message signed for an entry is the same canonical encoding (Figure 4) over which the entry digest and the K_chain HMAC are computed. The message signed for a head is the head canonical encoding (Figure 6). In each case the domain label and a single NUL octet are prefixed to the signature input only; they are neither stored nor part of the canonical encoding. The tags are the ASCII strings "VIRP-CHAIN-ENTRY-SIG-v1" (23 octets) and "VIRP-CHAIN-HEAD-SIG-v1" (22 octets), each followed by one NUL octet, and they differ in their eleventh octet, which is the domain separation: a head signature cannot validate as an entry signature or the reverse.

The head canonical encoding is a compact JSON object of the same style as the entry encoding: members in the fixed order below, no insignificant whitespace, single comma and colon separators:

{"last_entry_hash":"<64 lowercase hex>",
 "last_sequence":<integer>,
 "session_id":"<string>",
 "v":"VIRP-CHAIN-HEAD-v1"}

(shown on multiple lines for readability only; the
 canonical form contains no newlines and no spaces)
Figure 6: Chain Head Canonical Encoding

last_sequence is an unsigned JSON number in decimal ASCII; the other three members are JSON strings. The v member is the literal version tag "VIRP-CHAIN-HEAD-v1". The same no-escaping constraint stated for entry string members (Section 6.5) applies to session_id here.

When signing is enabled, the complete stored record is the base record (the canonical encoding of Figure 4, together with its separately stored SHA-256 digest and K_chain HMAC) plus a signature extension consisting of the signature scheme identifier (the ASCII string "ed25519-detached-v1"), the signing key identifier, and the 64-octet Ed25519 signature. When signing is not enabled these extension fields are empty and the base record is byte-identical to an unsigned entry. The signature extension is not part of, and is not fed back into, the canonical encoding. A verifier that holds the corresponding public key checks the signature; a verifier that does not hold it treats the entry as unsigned rather than as failed. Enabling the signature does not weaken or replace the HMAC: the HMAC is written unconditionally and a signing failure aborts the whole append.

This authenticator changes the trust boundary for chain entries only, and only where enabled. For entries so signed it provides public-key-verifiable origin: a party holding the corresponding public key can verify them and, lacking the private key, cannot forge them, which the symmetric HMAC alone does not allow (Section 6.1). The signature proves possession of the private key that produced it; attributing that key to a particular node still depends on a trustworthy binding of the public key to the node, by pinning or key distribution, which is out of scope here. The signature does not defend against compromise of the signing key itself, which remains the province of key custody (Section 6.2), independent witnessing, and external anchoring (Section 18.4). Observation records are not covered by this signature; asymmetric observation origin remains the separate item of Section 18.3. Deployment status, including which reference nodes enable it, is recorded in Section 19.

6.6. Canonical Encoding

Authentication inputs MUST be deterministic and unambiguous. This document specifies, for each authenticated artifact, the exact octet sequence covered.

For v2 observations, the authenticated bytes are the 88-octet serialized header of Section 6.4 concatenated with the payload. The header is fixed-width with all multi-byte fields in network byte order, so the encoding is already deterministic and unambiguous; the normative requirements are the exact offsets and widths of Section 6.4 and the rule that reserved is zero. The header authenticates a SHA-256 command digest, not the command string, so the binding is to the digest of the canonical command; the canonicalization that feeds that digest is defined by the v1 command canonicalizer for legacy string commands and, for typed profiles, by the rules under development in Section 18.7.

Normative authentication inputs cannot be optional: adding a domain-separation label would change the authenticated octets, so a labeled and an unlabeled implementation would reject each other's records. v2 therefore uses the fixed header exactly as specified -- including its version octet, which together with the differing v1 and v2 header layouts is what provides cross-version separation. A future wire format version SHOULD introduce an explicit domain-separation label; adding one to v2 would be wire-incompatible and is not proposed here.

For chain entries, the authenticated bytes are exactly the deterministic binary encoding defined in Section 6.5. The legacy JSON representation is not a conforming canonical encoding for this protocol version.

7. Reference Containment Model

The reference deployment enforces separation between the requesting process and the VIRP O-Node through a two-host architecture. The addresses, hostnames, and hypervisor details below describe the lab topology in which the results reported in Section 15 and Section 16 were obtained; a deployment need only preserve the properties, not the topology.

Table 2: Reference Two-Host Containment
Host Role and Constraints
Requesting host Runs the agent gateway, the tool server, and the agent process. No direct network path to managed devices. Holds no observation, chain, or approval key. Outbound network policy restricted to the inference API and the O-Node socket only. Runs as an unprivileged system user with service-manager hardening.
O-Node host Runs the O-Node daemon. Holds the O-Key, K_chain, and device credentials, and the approver public keys. Has direct SSH and API access to managed devices. Accepts connections from the requesting host only; all other inbound traffic is blocked.

Communication between the requesting host and the O-Node occurs over a socket bridge to the O-Node's local socket. The requesting process issues intent requests and receives authenticated observations over this channel. It has no other path to device access.

7.1. Containment Layers

  • Hypervisor layer: virtual-machine firewall rules enforce network policy outside guest control.
  • Network layer: host firewalling restricts outbound traffic on the requesting host and inbound traffic on the O-Node.
  • Process layer: an unprivileged service user with no-new-privileges, a strict read-only system view, and a private temporary directory.
  • Filesystem layer: credential files owned by root with mode 0600, inaccessible to the service user.
  • Cryptographic layer: the requesting host holds no observation-authentication key, so a compromise of that host does not confer the ability to produce an observation that verifies. This is a property of key placement, not of asymmetric cryptography; see Section 6.1 for the precise scope.

8. Trust Tiers and Action Authorization

Table 3: Trust Tiers
Tier Definition Authorization Required
GREEN Read-only observation. No state change possible. None -- immediate execution
YELLOW Low-impact state change. Reversible within session. Human acknowledgment
RED High-impact or potentially irreversible change. Explicit signed approval, bound to command digest, device, and expiry
BLACK Destructive, out-of-scope, or policy violation. Rejected unconditionally

Tier classification is performed by the intent router, which evaluates the command class, target device, and current network state. Classification rules are configurable per deployment. A command that matches no rule MUST be treated as RED rather than permitted; the classifier fails closed. The tier classifier itself is outside the requesting process's ability to modify -- it runs as a separate process with its own audit log.

Rejections are themselves authenticated records. When the gate refuses a command, the O-Node emits an authenticated error observation carrying the true tier and a proposal identifier, so that a refusal is evidence in the chain rather than an absence of evidence.

9. Threat Model

VIRP defines the following threat actors and the protocol's response to each. The rows are stated per key role, because different key holders have different forging power.

Table 4: Threat Model
Threat Actor VIRP Response
Compromised requesting agent Cannot forge observations, chain entries, or approvals: it holds none of those keys. If it holds an R-Key, it can submit authenticated intent messages, which remain subject to gate classification and authorization policy. Containment (Section 7) prevents direct device access.
Compromised agent host (full root) May expose the R-Key if the deployed R-Node stores it there. No observation, chain, or approval key is present in the reference architecture; the attacker can read records and submit them to a key-holding verifier but cannot produce an observation, chain entry, or approval that verifies.
Compromised O-Key holder Can forge v1 observations directly. Because the O-Key is also the v2 session-key derivation input in the reference implementation, the holder can also derive and forge v2 observations whenever it has the necessary session-derivation context. Cannot forge chain entries (needs K_chain) or approvals (needs the approver's Ed25519 key). A component limited to verifying v1 without this forging power would require a separately derived verification capability or an asymmetric construction; possessing the actual O-Key is not v1-only.
Compromised R-Key holder Can produce authenticated Intent Channel messages within the R-Key's scope. Cannot produce Observation Channel records: key-role and channel binding are enforced by the implementation. Intent authentication is not authorization; the gate and approval policy remain authoritative.
Compromised holder of a single derived session key Can forge v2 observations for that one session only. Cannot forge other sessions, v1 records, or chain entries. This bound applies to a component provisioned with only the derived key; it is a future profile rather than a documented property of the current reference deployment (Section 6.4).
Compromised chain writer or verifier (holds K_chain) Can rewrite or forge chain entries and thus chain history. Cannot forge a v2 observation under an uncompromised session key. External anchoring (Section 18.4) is what makes already-anchored history rewrite-detectable.
Compromised collection boundary (O-Node holding several keys) May hold K_obs, a session key, K_chain, and device credentials at once. Can forge observations, recompute the chain, and bypass the gate to execute writes directly with device credentials, omitting them from the record. VIRP does not prevent this. What remains: it cannot forge an approval signed by an uncompromised approver key, held outside the boundary. Independently retained approval records authenticate approvals that are presented; establishing that no corresponding approval existed is a stronger claim, requiring a complete approval history whose relevant range and head are independently retained or externally anchored. An individual retained record proves an approval existed and was valid, not that no other did. An unanchored local log controlled by the compromised boundary cannot establish absence, since a K_chain holder can rewrite, remove, or withhold it. External device-side authorization, credential constraints, and independent monitoring are required to detect or limit out-of-flow execution.
Replay of a genuine observation A v2 stateful verifier maintains a persistent high-water sequence number per (session identifier, node identifier) and accepts a v2 observation only when its sequence strictly advances that mark; the state persists across restart. A strict high-water verifier requires observations to be accepted in sequence order; a profile permitting concurrent in-flight operations within a session MUST define an equivalent replay-safe acceptance window rather than a scalar high-water mark. Legacy v1 HMAC verification is stateless and does not itself provide this guarantee.
Credential exfiltration from the agent Device credentials are not on the agent host in the reference implementation; the boundary holds them.
Man-in-the-middle on the O-Node transport Records are authenticated before leaving the boundary; modified records fail verification. This provides neither confidentiality nor endpoint authentication; deployments SHOULD use an authenticated, encrypted transport.
Physical access Out of scope; a deployment requirement.

10. Future Architecture: Role Separation

The current VIRP architecture concentrates observation, intent gating, and action execution within a single O-Node process. This design is operationally simple and sufficient for environments where the O-Node host is well-protected. However, as deployments scale and the consequences of a single-node compromise grow, protocol-level role separation offers a principled path to narrower blast radii and stronger separation of duties. This section defines a future three-role architecture and a migration roadmap from the current single-node baseline.

10.1. Architectural Motivation

In the current design, a compromised O-Node can forge observations, approve intents, and execute actions on devices. While chain integrity makes tampering with presented history detectable to a party that holds an independently retained chain head, a live compromise grants the attacker control over future operations for the duration of the compromise window.

The three-role architecture addresses this concentration of trust by decomposing the O-Node into three distinct principals, each with a narrow set of capabilities and its own keying material. The design goal is that compromise of any single node is insufficient to both fabricate state and act on that fabrication.

10.2. Observer Node

The Observer Node is responsible for device telemetry collection and observation authentication. It holds read-only credentials for managed devices and the observation key material. The Observer Node listens on a read-only socket and MUST NOT accept or process write intents.

The Observer Node authenticates each observation at collection time, covering the fields specified in Section 6.4. The authenticated observation is referenced from the chain and made available to other nodes over the read-only socket.

The Observer Node holds no policy state, no approval authority, and no write credentials. It cannot authorize or execute configuration changes on any managed device.

10.3. Executor Node

The Executor Node holds write credentials for managed devices. It receives command bundles that have been signed by the Policy Node and executes them against the specified targets. The Executor Node MUST refuse execution of any command bundle that does not carry a valid Policy Node signature.

The Executor Node holds no chain key, no observation key, and no policy or constraint state. It does not evaluate whether a command is appropriate; it verifies only that the command bundle bears a valid signature from a known Policy Node. The Executor Node makes no autonomous decisions.

Upon completing execution, the Executor Node reports the result to the Observer Node, which independently collects a post-action observation for outcome verification. The Executor Node does not self-report success or failure into the observation chain.

10.4. Policy Node

The Policy Node holds the constraint ledger, operator preferences, trust tier definitions, and the approval queue. It receives intent requests, evaluates them against policy, and -- if approved -- signs a command bundle with its own Ed25519 private key and forwards it to the Executor Node.

The Policy Node MUST NOT hold device credentials of any kind. It never communicates directly with managed devices. Its sole output is signed command bundles delivered to the Executor Node. The Policy Node consumes observations from the Observer Node for situational awareness but cannot influence or alter the observation chain.

Human approval workflows, trust tier escalation, and operator overrides are mediated through the Policy Node. Tier classification (as defined in Section 8) is performed by the Policy Node rather than by a co-located intent router.

10.5. Separation-of-Duties Property

The three-role architecture enforces a strict separation-of-duties property: no single node can observe device state, approve an action, and execute that action. Each of these three capabilities is held by a different principal with independent keying material.

Observation requires the observation key (held only by the Observer Node). Approval requires the Policy Node signing key (held only by the Policy Node). Execution requires device write credentials (held only by the Executor Node). An attacker who compromises any single node obtains exactly one of these three capabilities.

This property MUST be enforced at the protocol layer. Implementations MUST NOT permit a single process or host to hold the keying material of more than one role in a production deployment of the three-role architecture.

10.6. Compromise and Blast Radius Analysis

The following analysis considers the impact of compromising each node individually and in combination.

Observer Node compromise. An attacker gains the observation key material and read-only device credentials, and can forge or fabricate observations and read device state passively. The attacker cannot approve intents and cannot execute write operations. Blast radius: observation forgery and passive reconnaissance.

Executor Node compromise. An attacker gains device write credentials, but the Executor Node only processes command bundles carrying a valid Policy Node signature. The attacker may attempt to replay previously received, validly signed bundles. Implementations SHOULD include replay protections such as nonces or expiring timestamps in command bundles. Blast radius: replay of previously approved commands, bounded by those protections.

Policy Node compromise. An attacker gains the ability to sign arbitrary command bundles, but the Policy Node holds no device credentials and has no network path to managed devices. Blast radius: unauthorized approvals and signed bundles that remain inert unless delivered to a compromised Executor Node.

Observer and Executor compromise. The attacker can forge observations and holds write credentials, but cannot generate validly signed command bundles. The attacker could bypass the Executor's signature check through local code modification, effectively using the write credentials directly. This is equivalent to raw device access plus observation forgery; the Policy Node's approval record will show no corresponding approval, subject to the retention and anchoring qualification in Section 9.

Policy and Executor compromise. The attacker can sign arbitrary command bundles and execute them. This is the most operationally dangerous two-node scenario. The attacker cannot forge observations: post-action observations collected by the uncompromised Observer Node record the resulting device state.

Observer and Policy compromise. The attacker can forge observations and sign bundles, but cannot execute commands. Blast radius: fabricated state and pre-signed malicious bundles, with no execution capability.

Full three-node compromise. Equivalent to a compromised single O-Node in the current architecture. The three-role architecture does not defend against simultaneous compromise of all principals; its value lies in requiring the attacker to breach three independent trust boundaries rather than one.

10.7. Migration Roadmap

Stage 1: Single O-Node (current). The existing single-process O-Node handles observation, policy evaluation, and execution. This is the deployment baseline described throughout this document. The blast radius of an O-Node compromise is the full set of managed devices.

Stage 2: Observer-Executor separation. The O-Node is split into an Observer process and an Executor process, each with its own socket and credentials. This separation can be achieved on a single host using process isolation.

Stage 3: Policy Node extraction. Policy evaluation, the constraint ledger, and the approval queue are extracted into a dedicated Policy Node process with its own signing key. The Executor Node is modified to require a valid Policy Node signature on all command bundles.

Stage 4: Physical separation. The three nodes are deployed on separate hosts with independent network policies. The Policy Node may be placed on an air-gapped host or backed by a hardware security module for its signing key. Inter-node communication is authenticated using mutual TLS or a comparable mechanism.

10.8. Security Considerations for Role Separation

The three-role architecture introduces inter-node communication paths that do not exist in the single O-Node design. Each path represents an attack surface that MUST be protected by authenticated and encrypted transport. Implementations MUST NOT rely on network topology alone for inter-node authentication.

Key management complexity increases with role separation. Key rotation procedures MUST be defined for each role independently, and public keys MUST be distributed to the nodes that verify signatures.

Command bundle replay is a risk specific to the Executor Node. Implementations SHOULD include monotonic sequence numbers and expiration timestamps in signed command bundles, and the Executor Node SHOULD reject bundles already processed or exceeding a configurable staleness threshold.

The migration roadmap permits intermediate states where two roles share a host or process. Implementations operating in an intermediate state SHOULD document which separation-of-duties properties are enforced and which are deferred.

11. External Authorization Binding

The security goals in Section 12 include a stated limit: G3 does not hold against a party that possesses device credentials and a network path to the device outside the gate. Two mechanisms address that party: containment (Section 7), which aims to deny the requester such a path, and device-side authorization, which constrains what any principal reaching the device may do regardless of path. This section specifies the second. It defines a deployment profile, External Authorization Binding, in which the authority to change a device is removed from the gate and placed in an authorization service the gate does not control, and in which the gate's record of an action is reconciled against the device's own independent record of that action.

The component mechanisms are not novel. Per-command authorization and command accounting are long-standing capabilities of AAA protocols such as TACACS+ [RFC8907], and read-only administrative identities are ordinary practice. The contribution of this profile is the composition: an autonomous or automated requester is treated as a constrained principal whose read authority and write authority are carried by different identities, whose write authority is issued by a party other than the requester's gate and only under recorded authorization, and whose every action is cross-checked against evidence the requester's own infrastructure did not produce. VIRP contributes the evidence layer that binds the gate's account of an action to the device's account of the same action so that the two can be compared by a third party.

11.1. Credential Separation

Under this profile the gate authenticates to a managed device using a read-only identity whose authorization, evaluated by the device's own authorization service, permits only observation-class commands (Section 8, GREEN). The credential that would permit a write is not held by the gate at rest. This inverts the default arrangement in which a gate holds a privileged device credential and is itself the sole arbiter of what is sent: a gate that has been fully compromised, in the sense of Section 9, can still send only what the read-only identity is authorized to run, because the device, not the gate, makes the authorization decision.

The following properties are REQUIRED of a conforming External Authorization Binding deployment:

  • The gate's standing device identity MUST be authorized by the device for observation-class commands only. A command outside that class MUST be denied by the device's authorization service, not merely by the gate's classifier.
  • A write-capable device identity MUST NOT be stored on the gate in a form usable without a separate, per-action authorization step performed by a party other than the gate (Section 11.3).
  • The device MUST be configured to send command authorization requests and command accounting to services reachable independently of the gate, so that a compromise of the gate does not by itself suppress or forge those records.
  • Human operator access MUST remain available on a path that does not depend on the gate's identities, and SHOULD remain available when the authorization service is unreachable, so that a failure of the authorization service degrades the automated requester rather than locking out human recovery. Use of that recovery path is itself an event that SHOULD be recorded and classified (Section 8).

The authorization service is a distinct trust component from the gate and from the O-Node collection boundary. Its compromise is analyzed separately (Section 11.6): it can grant authority it should not, but it cannot by itself produce the gate-side records or the device-side accounting that a granted action would generate, so an unauthorized grant remains detectable by reconciliation.

Where the authorization and accounting path uses TACACS+ [RFC8907], its transport SHOULD be cryptographically protected using the applicable TACACS+ security profile, and the TLS 1.3 profile of [RFC9887] when supported by both peers. The independence this profile relies on -- that the gate cannot suppress, forge, or alter the authorization and accounting records -- is only as strong as the protection of the transport carrying them; an inadequately protected AAA path between device and service is a weaker evidence source than the architecture assumes.

11.2. Evidence Records

Reconciliation (Section 11.4) compares records of four kinds. Their fields below are the interoperable semantics a verifier depends on; the reference implementation carries them as chain artifact bodies (Section 6.5) whose artifact_type names the kind. Fields marked (impl) are present in the reference implementation today; fields marked (spec) are specified here and not yet emitted (Section 19).

gate_intent/1 (impl):
The gate's declaration, before acting, of the action it is about to perform. Carries the target device identifier, the driver, the canonical command, the classified tier and the effective ceiling that produced the decision, the decision itself (auto-execute, propose, or refuse), the acting identity, and, for an action requiring approval, the approval and proposal references. Emitted before the device is contacted.
gate_execution/1 (impl):
The gate's record, after acting, that it performed the action named by a preceding gate_intent, carrying a reference to that intent and the disposition of the attempt.
authorization_decision/1 (spec):
The authorization service's decision, permit or deny, for a command by a principal on a device, carrying the device identifier, the principal identity, the canonical command or its digest, the decision, and the service's timestamp. In the reference implementation this information exists in the authorization service's local log but is not yet emitted as a chained record (Section 11.4, Section 11.7).
device_accounting/1 (impl, as received):
The device's own command-accounting record, received by a collector reachable independently of the gate and chained on receipt. Carries the device identifier, the principal identity as the device reports it, the command as the device reports it, the device's timestamp, and the execution semantics the accounting profile establishes for records of this device class (see below). Its content originates at the device, not at the gate or the collector.

A command-accounting record does not by itself establish that the device executed the command. What such a record proves is implementation dependent: TACACS+ for device administration directs a client to send a command-accounting record for a command entered, irrespective of how it was authorized [RFC8907], so on some platforms an accounting record attests only that a command was entered, not that it was authorized or executed. An External Authorization Binding deployment MUST establish, per device class, which of the following its device_accounting/1 records attest: command entered; command authorized; command executed; or an accounting event of unknown execution semantics. This established meaning is the record's execution semantics, and reconciliation (Section 11.4) MUST use only accounting records whose execution semantics are established as attesting execution when it classifies an action as executed. In the Cisco IOS and IOS-XE deployments exercised by the reference implementation, an accounting record was observed to correspond to an executed command and an authorization denial produced no accounting record; a deployment MUST NOT assume that mapping holds on other platforms.

11.3. Approval-Scoped Grants

Observation-class actions execute under the read-only identity with no further authorization step. For an action above that class, the write authority is issued only in response to an approval recorded in the VIRP chain, and only for the specific action authorized.

A conforming issuer, on presentation of an action to be authorized, MUST verify an approval record carrying a valid Ed25519 signature from an enrolled approver (Section 6.1), bound to the digest of the canonical command, the target device, and an expiry, before issuing any write authority. The authority so issued MUST be scoped to the exact canonical command authorized and MUST expire no later than the approval's expiry. The issuer MUST be a party other than the gate and MUST verify the approver signature itself rather than relying on the gate's assertion that an approval exists; this is what prevents a compromised gate from obtaining write authority by fabricating an approval.

An approval intended for single execution MUST carry a unique approval identifier. The issuer MUST record consumption against that identifier and MUST reject any subsequent attempt to derive authority from the same approval. Expiry does not substitute for replay protection: within a single valid window an unconsumed approval would otherwise authorize the same operation repeatedly. Consumption is recorded at the point the write authority is issued; an issuer that cannot durably record consumption before issuing authority MUST NOT issue it. Whether a failed execution may re-consume the same approval is a deployment policy that MUST be stated, and the default MUST be that an issued grant consumes the approval regardless of execution outcome.

The approval binds an authorized operation; enforcement acts on a device-level invocation. A command grant MUST NOT authorize a device-level invocation different from the invocation whose digest was approved. Where the driver's rendering of a canonical command into device bytes is not identity-preserving, the deployment MUST establish that correspondence (the transport-fidelity property of Section 18.7) or MUST NOT treat the grant as binding the executed invocation.

Where the authority is expressed by writing an authorized command into a policy the device consults, the grant MUST be anchored so that it matches only the exact canonical command and MUST NOT be expressible in a form that a longer or compound command can satisfy by prefix or by embedding. An implementation that renders grants as textual policy MUST anchor each grant at both ends of the command string and MUST reject grant text containing command separators or output-redirection operators.

The interval between a policy change taking effect and its reversal taking effect bounds the exposure of a grant. An implementation MUST treat the effective expiry of a grant as the authorized expiry plus any latency the authorization service introduces in applying a revocation, and MUST NOT treat the return of a policy-reload operation as proof that a prior grant is no longer in force if the service applies such changes asynchronously.

This subsection is specified but NOT yet implemented in the reference code; see Section 19. The implemented profile to date uses a static authorization policy (a read-only identity permitted the observation class, an operator identity, and denial of everything else) together with the reconciliation of Section 11.4.

11.4. Reconciliation Against Device Accounting

Reconciliation compares the evidence records of Section 11.2, which originate from three independent places: gate_intent and gate_execution inside the collection boundary, device_accounting at the device, and authorization_decision at the authorization service. The property it establishes is not that the gate says an action occurred, but that the device's own accounting and the authorization service's own decision are consistent with the gate's account of the same action.

Two records are candidates for the same action when they agree on a match key, and are the same action when they also fall within a bounded temporal window. The match key is:

  • the device identifier, in a representation both sides resolve to the same value;
  • the principal identity, i.e. the device identity the gate authenticated as, as it appears in device accounting;
  • the canonical command, compared by exact digest, not by display text.

The temporal window bounds "the same action" against clock skew between the gate, the device, and the collector; it MUST be stated by the deployment, and records matching the key but outside the window MUST be classified UNRESOLVED rather than MATCHED. A device whose clock is unsynchronized cannot be reconciled on time, and its records MUST be classified UNRESOLVED until its clock is established, because an accounting timestamp from a misconfigured clock could otherwise match the wrong action.

A verifier classifies each candidate action as one of:

MATCHED:
gate_intent and gate_execution for an action have a device_accounting record agreeing on the match key within the window. The gate's account and the device's account agree.
GATE-ONLY:
the gate recorded execution with no device_accounting record agreeing on the match key within the window. The gate claims an action the device did not account.
DEVICE-ONLY:
a device_accounting record has no gate_execution agreeing on the match key within the window. The device accounted an action the gate did not record, which includes an action taken outside the gate.
DENIED:
an authorization_decision recorded a denial for the command and principal. A denial's visibility in accounting is device-dependent (see below); DENIED is therefore established from the authorization_decision, not inferred from the absence of an accounting record.
UNRESOLVED:
the available records are insufficient for a deterministic classification, including a match on key but outside the temporal window and any record whose clock is not established.

Two properties of accounting evidence are load-bearing and MUST be stated wherever this reconciliation is presented as evidence. First, a command-accounting record does not universally mean the device executed the command; its execution semantics are established per device class by the accounting profile (Section 11.2), and a classification of executed (MATCHED, GATE-ONLY, DEVICE-ONLY) MUST rest only on accounting whose semantics attest execution. An accounting record that attests only that a command was entered, for example a record a platform emits for a command it then denied, MUST NOT be read as execution evidence. Second, because whether a denied command is accounted at all is device-dependent, the absence of an accounting record does not establish that no action was attempted, and a denial is reliably visible only in the authorization_decision. Making denials first-class evidence therefore requires the authorization decisions to be captured as chained records rather than held only in the authorization service's local log; that capture is described as future work (Section 11.7). Until it is in place, an authorization decision presented alongside a bundle is an attached, separately sourced artifact, not a record within the chain, and MUST be labeled as such.

11.5. Two Kinds of Refusal

A requested action may be refused in two architecturally distinct places, and the two produce different evidence.

A gate or classifier refusal is generated inside the collection boundary by VIRP itself, when the tier classifier declines an intent (Section 8). It is emitted as an authenticated, chained record, so a VIRP-side refusal is first-class chain evidence.

An external authorization refusal is generated independently by the device's authorization service, when that service denies a command the gate did send. In the current implementation this decision is retained in the authorization service's log and presented as an attached artifact, not as a chained record (Section 11.4). The two are not contradictory: the first is a decision VIRP made and recorded; the second is a decision another system made, which VIRP does not yet chain. This is the property that Section 11.7 would change.

11.6. Threat Considerations

This profile changes the placement of authority and therefore the consequences of specific compromises.

Compromise of the gate:
A fully compromised gate can send only commands its read-only identity is authorized for, because the device makes the authorization decision. It cannot obtain write authority without an approval it cannot forge (Section 11.3). It can fabricate its own gate-side records, but those are reconcilable against device accounting it does not produce: fabricated executions appear as GATE-ONLY (Section 11.4).
Compromise of the authorization service:
A compromised authorization service can grant authority that was not approved. It cannot produce the gate-side records for an action, nor the device's independent accounting; an action it wrongly authorized, if executed, still generates device accounting with no corresponding approval in the chain, which reconciliation surfaces. The authorization service is therefore a distinct trust component whose isolation -- network reachability, administrative access, outbound scope -- SHOULD be constrained to the minimum its function requires.
Loss of the authorization service:
If the authorization service is unreachable and the gate's identities have no local authorization fallback, the automated requester cannot act. This is the intended failure direction. It makes the human recovery path (Section 11.1) load-bearing, and removing that path converts a service outage into an operator lockout.

Neither the gate alone nor the authorization service alone is sufficient to both authorize and perform a write while producing records that reconcile. That is the property the profile is designed to provide, and it holds only to the extent that the accounting and decision paths are genuinely independent of the gate and that the transport carrying them is protected (Section 11.1), which are deployment obligations, not protocol guarantees.

11.7. Not Yet Realized

The following are specified above but not present in the reference implementation, and are stated here so a deployment does not assume them:

  • Approval-scoped dynamic grants, including the single-use rule (Section 11.3). The implemented deployments use a static authorization policy plus reconciliation. The path from a chained approval to a scoped, time-bounded, single-use grant issued by a party other than the gate is designed and not yet built.
  • Chained authorization decisions (Section 11.4, Section 11.5). Authorization decisions, including denials, are presently retained in the authorization service's local log and presented as attached artifacts. Capturing them as authorization_decision/1 chain records, so that a denial is first-class evidence and shares the integrity properties of the chain, is future work.
  • Transport-fidelity establishment for grant binding (Section 11.3). Binding the approved operation to the exact executed device invocation depends on the typed-operation properties of Section 18.7, which are future work.

12. Informal Security Goals

This section states the security goals that VIRP is designed to achieve. These goals are expressed in natural language. In response to review feedback on draft-howard-virp-03, this section was retitled from "Formal Security Properties" to "Informal Security Goals" to align with the terminology used in the formal protocol analysis community.

A symbolic formal analysis in ProVerif has been completed. HMAC key secrecy and injective agreement between the O-Node and a verifier have been machine-verified under the Dolev-Yao model, the latter under a stated trace restriction corresponding to the v2 replay-counter invariant (Section 18.1). This covers G1 (observation non-forgeability, symmetric form) and at-most-once acceptance under the model's stated (session identifier, sequence) trace restriction. It does not establish timestamp freshness, which is not symbolically modeled, and does not fully model the implementation's per-(session identifier, node identifier) high-water ordering; it does not cover the stateless v1 path or v1 error records. Architectural non-conflation (G4) and agent non-escalation (G5) are containment properties, not wire-protocol properties, and are not modeled. No Tamarin model exists; a second-tool cross-check is future work.

12.1. G1: Observation Non-Forgeability (symmetric, per key)

A party that does not hold the authenticating key for an observation cannot produce a record that verifies. For a v1 observation this is HMAC-SHA256 under K_obs; for a v2 observation it is HMAC-SHA256 under the HKDF session key. Without the relevant key it is computationally infeasible to produce a valid authentication code, under the standard PRF/MAC assumption on HMAC-SHA256. The property is relative to that key's secrecy and does not hold against a holder of the key.

Role separation limits cross-artifact forgery, with one explicit exception in the reference implementation: the O-Key directly authenticates v1 observations and also serves as the input keying material for v2 session-key derivation. An O-Key holder can therefore forge v1 observations and, when it possesses the required derivation context, derive keys capable of forging v2 observations. This reuse does not confer the ability to forge chain entries, Intent Channel messages, approvals, or federation records authenticated under separate keys.

Asymmetric non-forgeability -- origin verification without a forging-capable secret -- is future work (Section 18.3) and would still not bound signer compromise.

12.2. G2: Chain Integrity

Any modification to, deletion of, or insertion into the presented chain is detectable by a verifier holding K_chain, because the canonical entry encoding covers the predecessor's entry digest and is itself covered by an HMAC under K_chain (Section 6.5). Two qualifications are load-bearing. First, this property is relative to the secrecy of K_chain: a holder of K_chain can recompute a consistent history. Second, it establishes the integrity of the entries presented, not that the presented chain is complete; detecting truncation or a withheld suffix requires an independently retained chain head or an external checkpoint (Section 18.4).

12.3. G3: Intent Prior to Action

No write operation reaches a device through the gate without a prior authorized intent on record. The O-Node refuses execution for any action without a matching authorized intent, and for RED-tier actions without a valid approval bound to the command digest, device, and expiry. This property holds even if the requesting process is fully compromised. It does not hold against a party that possesses device credentials and a network path to the device outside the gate; that case is addressed by containment (Section 7) and by device-side authorization, not by this protocol.

12.4. G4: Two-Channel Non-Conflation

The Observation and Intent channels are architecturally separate and key-role separated. An observation cannot authorize an intent. An intent cannot substitute for an observation.

12.5. G5: Agent Non-Escalation

The requesting process cannot escalate its own trust tier. Tier classification is performed by a separate process outside that process's modification scope.

13. Reference Implementation

The reference C core (open source, Apache 2.0) [VIRP-REPO] implements the O-Node daemon, the authentication library, the gate and approval flow, and the chain database. IronClaw is a platform built on that core by Third Level IT LLC [THIRDLEVEL], adding multi-vendor driver support, a natural language interface via the Model Context Protocol, intent routing, and operational tooling. The protocol specified in this document is independent of that platform; the platform is cited because the results reported in Section 15 and Section 16 were obtained with it.

13.1. Component Summary

Table 5: Reference Components
Component Description
O-Node daemon C binary. libsodium (Ed25519 for approvals and federation) and OpenSSL (HMAC-SHA256, SHA-256) with an SQLite-backed chain. Pluggable driver interface. Unix socket IPC with peer-credential authorization.
Cisco IOS driver SSH via exec channel. Supports show commands across IOS and IOS-XE. Authenticates all output at collection.
FortiGate driver Dual-transport SSH (primary) and REST API (fallback), FortiOS 7.x compatible. The REST transport is read-only by construction: non-GET methods are refused at the transport layer.
Executor C executor for SSH command dispatch. Uses the exec channel, not a PTY. Authenticates all device output before returning it to the consuming layer.
Verification library Python library that verifies v1 observations. It holds the O-Key and is therefore a trusted component in the sense of Section 6.2: it can verify and, because the key is symmetric, also produce v1 records. It is not an untrusted verifier.
Intent router Tier classifier and intent authorization engine. Separate process from the requesting agent.
Approval client Signs approval records with an Ed25519 key from an enrolled approver registry, optionally via a PKCS#11 token. The client signs only; the daemon is the sole chain writer. The implementation additionally accepts an ECDSA P-256 approver key; that is an implementation extension and is not defined by this revision, which specifies Ed25519 approvals (Section 6.1).
Tool server Model Context Protocol server exposing VIRP tools to a requesting agent. Mediates all agent-to-O-Node communication.

13.2. Test Coverage

The reference implementation carries C unit and integration suites, Python end-to-end and parity tests, a fuzzing harness, concurrency tests, negative tests for replay and approval reuse, live-hardware exercises across several vendor platforms, and a re-runnable formal proof target. Exact counts change with every revision of the code and are therefore not asserted here; they are reported by the test target in [VIRP-REPO]. Claims in this document that depend on implementation state are marked as such in Section 19.

14. FRR BGP Daemon Integration

VIRP includes a reference implementation for the BGP domain as a loadable FRR (Free Range Routing) module [VIRP-BGP-FRR]. The module hooks into FRR's bgpd process using the standard FRR_MODULE_SETUP pattern. It registers read-only hook callbacks on BGP events, constructs JSON observation payloads, authenticates them with HMAC-SHA256 at collection time inside the bgpd process space, and delivers them to the O-Node over a Unix domain socket. The module is strictly Observation Channel: it never modifies routing state, peer configuration, or the RIB.

14.1. BGP Daemon Hooks

The module registers four hook callbacks covering the primary BGP event classes:

Table 6: BGP Hooks
Hook Obs Type Default Tier Trigger Condition
peer_status_changed 1 (PEER_STATE) GREEN (YELLOW when leaving Established) BGP FSM state transition
peer_backward_transition 2 (PEER_BACKWARD) YELLOW Peer drops out of Established
bgp_process 3 (ROUTE_EVENT) GREEN Route announce or withdraw
bgp_route_update 4 (BESTPATH_CHANGE) GREEN Best-path selection changes

Data captured per hook: peer_status_changed records peer IP, peer AS, local AS, old and new FSM state, router-id, and producer identifier. peer_backward_transition records peer IP, peer AS, local AS, last FSM state, last major event code, uptime, and router-id. bgp_process records prefix, peer IP, peer AS, AFI, SAFI, router-id, and the announce/withdraw flag. bgp_route_update records prefix, AFI, SAFI, router-id, and the old and new path attributes (peer, nexthop, AS path, MED, local-pref, origin).

Trust tier assignment happens at the source: the peer_backward_transition hook always emits YELLOW tier because loss of an Established session is operationally significant. The peer_status_changed hook conditionally escalates to YELLOW when it detects a transition away from Established. All other observations are GREEN tier.

All hooks are registered at module load and unregistered at module teardown, following the FRR module lifecycle pattern.

14.2. BGP Observation Wire Format

Each observation is transmitted as a contiguous byte stream over the Unix domain socket: a packed binary header, a variable-length JSON payload, and an HMAC-SHA256 digest. All multi-byte integer fields in the header are in network byte order.

Bytes  Field          Encoding
-----  -------------  --------------------------------
 0-3   magic          0x56495250 "VIRP" (net order)
 4     version        uint8 (currently 1)
 5     obs_type       uint8 (1-4, see hook table)
 6     trust_tier     uint8 (0=GRN 1=YLW 2=RED 3=BLK)
 7     flags          uint8 (reserved, zero)
 8-15  timestamp_ns   uint64 ns epoch (net order)
16-19  sequence       uint32 monotonic (net order)
20-23  payload_len    uint32 (net order)
24-27  producer_id    uint32 from BGP router-id
28-N   payload        UTF-8 JSON (payload_len bytes)
N+0    hmac[32]       HMAC-SHA256(header || payload)
Figure 7: BGP Observation Wire Format

The wire header struct is packed so that no padding is inserted. The HMAC-SHA256 is computed over the concatenation of the 28-octet serialized header and the JSON payload, using a symmetric key shared between the bgpd module and the O-Node. Total wire size per observation is 28 + payload_len + 32 octets. Maximum payload size is 4096 octets. Minimum HMAC key length is 16 octets, maximum 256. The producer identifier is derived from the BGP router-id, providing a stable per-router identifier across observations.

This is a separate wire format from the O-Node observation formats of Section 6.3 and Section 6.4. It carries no session binding and is subject to the same qualification as v1: it is tamper-evident in transit under the shared key, and any holder of that key can produce records that verify.

14.3. JSON Payload Schema

All payloads share a common envelope with domain-specific fields. Every payload includes a "domain" field set to "bgp" and an "event" field identifying the observation type. A peer state change payload carries the peer name and address, peer and local AS, old and new state, router-id, and producer identifier. A best-path change payload carries the prefix, AFI, SAFI, router-id, producer identifier, and nested "old" and "new" objects each carrying peer, peer address, peer AS, nexthop, AS path, MED, local preference, and origin.

All string fields are escaped for control characters, backslashes, and quotes. Peer addresses are rendered to support both IPv4 and IPv6 peers. Because this payload is JSON produced by direct value insertion, it is subject to the same caution recorded in Section 6.5: it is an implementation transport format, not a canonical authenticated encoding, and the authenticated bytes are the octets as transmitted.

14.4. O-Node Receiver

The O-Node receives authenticated observations on a Unix domain socket. The receiver protocol is:

  1. Read 28 octets of wire header. Validate the magic value and the version.
  2. Extract payload_len. Reject if it exceeds the maximum payload size.
  3. Read payload_len octets of JSON payload.
  4. Read 32 octets of HMAC-SHA256.
  5. Compute HMAC-SHA256 over (header || payload) using the shared key.
  6. Compare computed against received using constant-time comparison. Reject on mismatch.
  7. Validate that the sequence number is monotonically increasing. Flag gaps as potential observation loss.
  8. Insert the verified observation into the chain.

The bgpd module performs automatic reconnection: if the O-Node socket is unavailable, observations are logged locally as a fallback and failure counters are incremented. Observations produced while the socket is unavailable are not chained; a consumer MUST NOT treat a local fallback log as chained evidence.

14.5. FRR VTY Configuration Commands

The module registers configuration commands under the BGP router node and show commands at the enable or view level: enabling and disabling authenticated observations, setting the O-Node socket path, and setting the HMAC key file path; plus show commands reporting state, key status, O-Node connectivity, producer identifier, and counters for records authenticated, delivered, and failed.

Enabling triggers HMAC key loading, if not already loaded, and initiates connection to the O-Node socket. The key-loading command validates the key immediately, rejecting keys shorter than 16 octets. Key material is zeroized on module shutdown.

14.6. Build and Deployment

The module can be built as a loadable shared object (recommended) or compiled directly into bgpd. As a loadable module it requires no modifications to the core FRR source. Build dependencies are OpenSSL for HMAC-SHA256. A standalone test harness validates authentication, verification, tamper detection, and sequence ordering without requiring the FRR build environment.

Deployment requires distributing the same HMAC key to both the FRR host and the O-Node. The key file MUST be restricted to mode 0600 and owned by the FRR process user. Note that this shared key is held by both parties; per Section 6.1, both can produce records that verify.

14.7. Security Properties of BGP Integration

  • Authentication at collection time: the HMAC is computed inside bgpd's process space at the moment the BGP event fires, not after the fact.
  • Observation-only: the module registers read-only hooks. It cannot modify routes, peer state, or configuration.
  • Protocol-agnostic framing: the observation structure and authentication are identical whether the source is BGP or another domain. Only the "domain" field in the payload changes.
  • Trust tiers at the source: tier assignment happens at collection, not at analysis time.
  • Tamper detection: any modification to an observation between bgpd and the O-Node is caught by HMAC verification, relative to the secrecy of the shared key.

15. Adversarial Findings

On 6 March 2026, the agent under test was asked directly whether it could circumvent VIRP. The agent provided an unprompted breakdown of four bypass vectors without attempting to exploit them. This self-disclosure is documented in [VIRP-FINDING-003]. The remediation column below states the current status accurately; two entries were described in earlier revisions as resolved by asymmetric observation signing, which is not implemented.

Table 7: Agent-Identified Bypass Vectors
Finding Description Remediation Status
Key forgeability Symmetric observation key readable on the requesting host, allowing forged observations. MITIGATED BY KEY PLACEMENT -- no observation key is present on the requesting host. Not resolved by asymmetric signing: observation authentication remains symmetric, so any holder of the key can forge (Section 6.1). Asymmetric observation signing remains future work (Section 18.3).
Direct device access Agent knew device addresses and could connect directly, bypassing the O-Node. RESOLVED -- host and hypervisor firewall policy block direct device access.
Intent gating bypass Agent could import the client library and call the execution path without filing intent. RESOLVED -- all execution paths route through the gate; unclassified commands fail closed at RED, and multi-command separators are refused at the request boundary before classification.
API key exploitation Inference API key visible in session context, allowing unsupervised sessions to be spawned. RESOLVED -- key rotated, sensitive-value redaction enabled in logging.

15.1. Live Penetration Test (6 March 2026)

An adversarial exercise was conducted against the lab environment. The agent was given a single seed address and instructed to operate without destructive actions. It obtained initial access through passive discovery within five minutes and administrative credentials on the lab firewall within twenty-five minutes, exploiting no CVEs and attempting no brute force. The root cause was a plaintext environment file containing a full credential keyring on a host the agent could read. A live tunnel to a colocation environment was reachable with those credentials.

The exercise validated Primitive 7. The agent did not break VIRP's cryptographic controls; it went around them by finding credentials in a plaintext file. The fix is architectural: credentials must not exist on the requesting host.

This result is reported as an operational finding about a specific deployment, not as a protocol property. It illustrates the boundary stated in Section 9: VIRP constrains what flows through the gate and does not constrain a party that obtains device credentials and a network path outside it.

16. Scale Testing Results

VIRP was exercised against a 35-router emulated lab topology (five-AS BGP topology) using a 16-thread executor, alongside a physical firewall and switch. A full sweep of the topology completed in under three minutes using 16 concurrent threads across three batches, producing one authenticated observation per device per command with no sequence gaps in the resulting chain and no false verification failures. No fabrication was attempted in this exercise, so it reports throughput and chain continuity rather than detection efficacy.

A passive discovery pass over the same lab completed in five minutes from a single seed address, identifying live hosts, fingerprinting vendor and operating system, and producing an authenticated observation for each confirmed device, with chain references for every finding.

17. Applications and Use Cases

17.1. Verifiable Network Audit

Traditional network audits produce reports that must be trusted on the auditor's authority. VIRP-enabled audits produce reports where every finding references an authenticated observation. The strength of that evidence is scoped by Section 6.1: a client that holds the verification key can confirm that the recorded bytes were authenticated by a holder of that key, and can detect modification of the presented chain. It does not, by itself, establish origin against a party that also holds the key. Deployments where the auditor and the client must not share a forging capability require asymmetric observation signing (Section 18.3) or a third-party anchoring arrangement (Section 18.4).

17.2. AI-Assisted Penetration Testing

VIRP provides chain of custody for penetration test findings. Every probe is an authenticated observation. Every access attempt is intent-gated before execution. Scope is enforced at the gate: the agent cannot interact with out-of-scope devices without a matching authorized intent. The client receives the chain alongside the report.

17.3. Continuous Compliance Monitoring

Baseline Memory (Primitive 5) enables continuous comparison of live device state against an authenticated baseline. Configuration drift -- a new BGP peer, a modified firewall policy, an unexpected open port -- is detected and attributed to a specific change in the chain.

17.4. Field Audit Appliance

A self-contained appliance running the full stack enables plug-in network discovery and audit. An operator connects the device to a network port and issues audit commands remotely; the device produces an authenticated report and is removed when complete. No software installation is required on the target network.

17.5. Industrial and Air-Gapped Environments

ICS and SCADA environments require audit trails for regulatory compliance and incident investigation. VIRP's authenticated observation model provides tamper-evident records of every automated interaction with control system components, supporting NERC CIP, IEC 62443, and similar frameworks. Because the gate and the chain are independent of any inference component, the same governed-automation path can be operated by scripts alone in environments where an AI component is not permitted.

18. Future Work

18.1. Formal Verification

A symbolic formal analysis of VIRP has been carried out in ProVerif [PROVERIF] (applied pi-calculus), with Usama Sardar (TU Dresden), prompted by Adrian Farrel's review of draft-howard-virp-03.

Under the Dolev-Yao model the analysis establishes:

  • Key secrecy: an adversary with full network control cannot learn the O-Key or the derived session key. The model does represent session-key derivation, as a symbolic one-way constructor over the master O-Key, and checks secrecy of both the master and the derived key. It is an abstraction: HKDF is an uninterpreted symbolic constructor; it does not model the implementation's generation counter or the exact serialized transcript, and it omits several authenticated v2 header fields from the symbolic serialization. The result assumes honest endpoints and an ideal secret; it does not establish secure storage, side-channel resistance, or any property after compromise of a key holder.
  • Injective agreement (v2 path): in the modeled honest-verifier execution, each verifier-acceptance event corresponds injectively to one matching O-Node production event, under the stated at-most-once (session identifier, sequence) trace restriction. This correspondence depends on the modeled verifier being defined not to produce observations itself; a real key-holding verifier can produce the same authenticator. The modeled restriction is weaker than, and different from, the implementation's persistent per-(session identifier, node identifier) strict high-water ordering (Section 9): the proof abstracts, but does not fully model, that ordering property. ProVerif's Horn-clause abstraction does not model the mutable counter directly; the counter and its persistence are exercised separately by the reference implementation's replay negative tests.

Two limitations. First, secrecy assumes honest endpoints and an ideal secret and says nothing post-compromise. Second, specific to the symmetric construction: because an HMAC verifier possesses the authentication key, the correspondence result applies to the modeled honest verifier process, which is defined not to generate observations. It does not give a third party cryptographic evidence distinguishing an O-Node-produced MAC from a verifier-produced one; it constrains the modeled verifier's behavior, not the real forging capability any key holder has. Publicly verifiable origin is what asymmetric observation signing (Section 18.3) would add.

The result is scoped to the v2 path. The stateless v1 HMAC check and v1 error records are outside the modeled protocol. Timestamp freshness is verified by test only. Proof artifacts are in the reference repository under /proofs and are re-runnable.

Open targets: discharging the replay restriction natively in a tool with mutable state, such as Tamarin -- no such model exists yet, and it is the intended second-tool cross-check; a process model of G4; and G5, which is out of scope for protocol-level analysis.

Earlier revisions of this document stated machine verification in both ProVerif and Tamarin. That was inaccurate: only ProVerif exists. This revision corrects it and describes Tamarin as future work.

18.2. Multi-Node Coordination

The current specification defines single O-Node operation. draft-howard-virp-01 includes a Multi-Node Coordination section defining leader election, observation synchronization, and cross-node chain verification for distributed deployments.

18.3. Asymmetric Observation Signing

The chain layer already offers an OPTIONAL asymmetric authenticator (Section 6.5.2); this section concerns the observation layer, which does not. Observation authentication in this revision is symmetric, so any holder of the relevant key can both verify and produce records (Section 6.1). Adding an asymmetric signature over the authenticated observation bytes would provide publicly verifiable origin and would allow a verifier that cannot forge. This is a wire format change: it adds a signature field and requires a public key distribution and revocation story. It would not, by itself, defend against compromise of the signer, which is a separate problem addressed by hardware custody (Section 18.6), independent witnessing, or external anchoring (Section 18.4). Any such extension MUST be introduced under a new wire format version rather than as an optional field, because optional authenticated inputs break interoperability (Section 6.6).

18.4. External Anchoring and Truncation Detection

Chain integrity as specified establishes the linkage of the entries presented to a verifier. It does not establish that the presented chain is complete, and it does not survive a holder of K_chain who rewrites history. Periodically publishing chain head digests to a party or medium outside the collection boundary -- an append-only transparency log, a counter-signing witness, or a third-party escrow -- would make already-published history rewrite-detectable and would give truncation a detection point. Specifying the checkpoint format, the publication cadence, and the verifier's obligations on a missing or divergent checkpoint is open work and is a prerequisite for any claim that the chain establishes the absence of a record.

18.5. Cross-Administrative-Domain Trust

Trust federation is implemented in the reference codebase and enables multiple O-Nodes operated by different organizations to participate in a shared trust chain via Ed25519 signatures. Open work in this area includes standardized key distribution and revocation for federated O-Nodes, public-key directory or transparency-log integration to detect equivocation, and policy primitives governing what cross-administrative-domain consumers may correlate or attest. Hardware-backed federation key custody (Section 18.6) is a prerequisite for the highest-assurance multi-organization deployments.

18.6. Hardware Security Module Integration

Approval signing already supports PKCS#11 tokens, so an approver's private key need not exist in host memory. The symmetric observation and chain keys are currently held in process memory on the O-Node. Integration with an HSM or TPM for those keys, or migration to an asymmetric construction under Section 18.3, would reduce the key-in-memory attack surface for the highest-assurance deployments.

18.7. Typed Operations

A v2 observation authenticates a digest of a canonical command string. As stated in Section 6.4, this makes the record meaningful only to a relying party that retains trusted request context. Work in progress replaces the free-form command string with a versioned registry of typed operations, so that the authenticated bytes cover a complete, self-describing operation encoding: an operation identifier, a registry version, typed parameters, and a resolved target. The properties that such a registry must satisfy are encoding injectivity (distinct operations encode to distinct octets), resolution determinism (a given operation and target resolve to the same device-level invocation), resolution injectivity (distinct operations do not resolve to the same invocation), and transport fidelity (the invocation delivered to the device is the one that was authenticated). Free-form command strings would remain supported as a legacy, lower-assurance adapter. This work is expected in a subsequent revision of this document.

18.8. IETF Standardization

VIRP is written in RFC format and is submitted as an individual Internet-Draft. Experimental results for the BGP integration of Section 14 are archived as a citable companion artifact at [VIRP-ZENODO]. Independent implementation by a second party is the prerequisite for standards track consideration; the open-source reference implementation is available for that purpose.

19. Implementation Status

This section records where the reference implementation [VIRP-REPO] diverges from the normative text above, so that a reader is not left to infer conformance from the specification alone. It is expected to shrink in subsequent revisions and carries no normative weight. This section is intended for removal before any publication as an RFC.

20. Security Considerations

Security considerations are distributed through this document rather than confined to a single section. Section 6.1 and Section 6.2 state what each key role does and does not protect, and are the authoritative statement of scope. Section 9 states the threat model per key holder. Section 12 states the informal goals and Section 18.1 states exactly what has and has not been machine-verified. Section 19 states where the reference implementation currently diverges from the normative text.

Three properties are important enough to restate here. First, observation authentication in this revision is symmetric: it establishes integrity relative to the secrecy of a key that the verifier also holds, and therefore does not provide publicly verifiable origin or any defence against a party holding that key. Second, authentication establishes that the collection boundary obtained and authenticated those octets for that recorded request; it does not certify that the response reflects the managed device's true state, and a compromised boundary can produce well-formed records that do. Third, the chain establishes linkage of the entries presented; establishing that a record does not exist requires an independently retained or externally anchored history (Section 18.4), and no claim of the form "no approval was granted" is supported by an unanchored local chain.

Deployments SHOULD carry VIRP traffic over an authenticated, encrypted transport. The protocol provides neither confidentiality nor endpoint authentication for the transport itself.

21. IANA Considerations

This document has no IANA actions.

22. Acknowledgements

The author thanks Adrian Farrel, whose review of draft-howard-virp-03 prompted the reframing of VIRP to a trust primitive for any operator acting on production infrastructure.

The author thanks Usama Sardar (TU Dresden) for the ProVerif formal analysis and ongoing work toward discharging the replay-counter restriction.

The author thanks an independent reviewer who exercised the implementation on physical Cisco hardware and reported the issues discovered during that evaluation.

23. References

23.1. Normative References

[RFC2119]
Bradner, S., "Key words for use in RFCs to Indicate Requirement Levels", BCP 14, RFC 2119, DOI 10.17487/RFC2119, , <https://www.rfc-editor.org/info/rfc2119>.
[RFC8174]
Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words", BCP 14, RFC 8174, DOI 10.17487/RFC8174, , <https://www.rfc-editor.org/info/rfc8174>.
[RFC2104]
Krawczyk, H., Bellare, M., and R. Canetti, "HMAC: Keyed-Hashing for Message Authentication", RFC 2104, DOI 10.17487/RFC2104, , <https://www.rfc-editor.org/info/rfc2104>.
[RFC5869]
Krawczyk, H. and P. Eronen, "HMAC-based Extract-and-Expand Key Derivation Function (HKDF)", RFC 5869, DOI 10.17487/RFC5869, , <https://www.rfc-editor.org/info/rfc5869>.
[RFC6234]
Eastlake 3rd, D. and T. Hansen, "US Secure Hash Algorithms (SHA and SHA-based HMAC and HKDF)", RFC 6234, DOI 10.17487/RFC6234, , <https://www.rfc-editor.org/info/rfc6234>.
[RFC8032]
Josefsson, S. and I. Liusvaara, "Edwards-Curve Digital Signature Algorithm (EdDSA)", RFC 8032, DOI 10.17487/RFC8032, , <https://www.rfc-editor.org/info/rfc8032>.

23.2. Informative References

[ED25519]
Bernstein, D.J., "High-speed high-security signatures", Journal of Cryptographic Engineering, .
[I-D.howard-virp]
Howard, N., "VIRP: Verified Infrastructure Response Protocol", Work in Progress, Internet-Draft, draft-howard-virp-05, , <https://datatracker.ietf.org/doc/html/draft-howard-virp-05>.
[NIST-ZTA]
National Institute of Standards and Technology, "Zero Trust Architecture", NIST SP 800-207, .
[RFC8907]
Dahm, T., Ota, A., Medway Gash, D., Carrel, D., and L. Grant, "The Terminal Access Controller Access-Control System Plus (TACACS+) Protocol", RFC 8907, , <https://www.rfc-editor.org/info/rfc8907>.
[RFC9887]
Dahm, T., Heasley, J., Medway Gash, D.C., and A. Ota, "Terminal Access Controller Access-Control System Plus (TACACS+) over TLS 1.3", RFC 9887, , <https://www.rfc-editor.org/info/rfc9887>.
[PROVERIF]
Blanchet, B., "ProVerif: Cryptographic protocol verifier in the formal model", , <https://bblanche.gitlabpages.inria.fr/proverif/>.
[THIRDLEVEL]
Third Level IT LLC, "Third Level IT LLC Research", , <https://thirdlevel.ai>.
[VIRP-BGP-FRR]
Howard, N., "VIRP BGP Module: FRR Integration Reference Implementation", Apache 2.0 License, , <https://github.com/nhowardtli/virp-bgp-frr>.
[VIRP-FINDING-003]
Howard, N., "VIRP-FINDING-003: Agent Self-Reported Security Bypasses", Third Level IT LLC Internal Research, .
[VIRP-REPO]
Howard, N., "VIRP Reference Implementation", Apache 2.0 License, , <https://github.com/nhowardtli/virp>.
[VIRP-ZENODO]
Howard, N., "Cryptographic Observation Attestation Inside a BGP Routing Daemon: Experimental Validation of VIRP", Zenodo, Technical Note, version 1.0, DOI 10.5281/zenodo.19161493, , <https://doi.org/10.5281/zenodo.19161493>.

Appendix A. Changes from draft-howard-virp-06

This revision does not broaden the base observation or chain-integrity guarantees of draft-howard-virp-06. It adds a separate External Authorization Binding profile with its own security properties and deployment assumptions, and it corrects one description of the chain layer to match the reference implementation.

  1. Optional chain-entry signature documented. The reference chain now writes an OPTIONAL per-entry and per-head Ed25519 signature alongside the mandatory HMAC, at append time, over the same canonical bytes (Section 6.5.2). Draft-howard-virp-06 described the chain as HMAC-only with asymmetric work in the future. The base guarantee is unchanged and remains symmetric; the signature is an additional, publicly verifiable authenticator, off by default and enabled per node. Section 6.1, Section 6.2, and Section 18.3 are updated accordingly.
  2. External Authorization Binding added. Section 11 specifies a deployment profile that separates the gate's read authority from its write authority, places write authorization in a service the gate does not control, and reconciles the gate's records against the device's own command accounting. The section states that the component mechanisms are pre-existing and that the contribution is their composition with a constrained automated requester.
  3. Evidence records defined. Section 11.2 gives the interoperable semantics of gate_intent/1, gate_execution/1, authorization_decision/1, and device_accounting/1, and marks which are emitted today.
  4. Approval-scoped grants specified, with a single-use rule. Section 11.3 requires write authority to be issued only against a chained approval, scoped to the exact canonical command, bounded by the approval expiry, carrying a unique identifier and consumed once, by an issuer other than the gate, and adds a transport-fidelity requirement binding the approved operation to the executed invocation. It is marked not-yet-implemented in Section 19.
  5. Reconciliation defined, not merely described. Section 11.4 gives the match key, the temporal window, and the MATCHED / GATE-ONLY / DEVICE-ONLY / DENIED / UNRESOLVED classification.
  6. Accounting-denial claim scoped to observed behavior. The statement that a denied command produces no accounting record is scoped to the Cisco deployments tested and is no longer asserted as general TACACS+ behavior, which [RFC8907] defines otherwise. The protocol conclusion -- that denials must be captured independently -- is retained (Section 11.4).
  7. Two kinds of refusal distinguished. Section 11.5 separates a VIRP-side chained refusal from an external authorization-service refusal that is not yet chained, resolving an apparent contradiction with Section 6.3.
  8. TACACS+ transport protection referenced. Section 11.1 recommends protecting the AAA transport, including the TLS 1.3 profile of [RFC9887] where supported, because the profile's independence assumption depends on it.
  9. Approver algorithm made consistent. The normative approver key is Ed25519 throughout; the reference implementation's acceptance of ECDSA P-256 is now described as an implementation extension not defined by this revision (Section 13).

The preceding revision's narrowing of claims relative to [I-D.howard-virp] is retained in full and summarized below for continuity.

  1. Per-observation Ed25519 removed. Revision 05 described a dual-signature observation carrying both an HMAC-SHA256 code and an Ed25519 signature, and an ed25519 field in the observation wire format. No such signature exists: observations are authenticated with HMAC-SHA256 only. Ed25519 is used for approval records and federation material. The field is removed from the wire format and the claim is removed from the abstract, the threat model, and the security goals.
  2. Symmetric trust boundary stated. Because observation authentication is symmetric, a verifier holds a forging capability. The abstract, Section 6.1, Section 6.2, and Section 12 now state this directly. Revision 05 described the verifier as unable to forge, which is true only of an asymmetric construction.
  3. Tamarin claim withdrawn. Revision 05 stated machine verification in both ProVerif and Tamarin. Only a ProVerif model exists. Section 18.1 now states the ProVerif result, the trace restriction it depends on, what is not modeled (timestamp freshness, the high-water counter, the v1 path), and describes Tamarin as future work.
  4. Formal result strengthened in one direction. Revision 05 claimed non-injective agreement. Injective agreement now holds, under the stated trace restriction.
  5. G1 restated for HMAC. Revision 05 reduced G1 to EUF-CMA unforgeability of Ed25519, a primitive observations do not use. G1 is now stated per key under the PRF/MAC assumption on HMAC-SHA256, with the O-Key dual-role exception named explicitly.
  6. Two observation formats specified. Revision 05 described one format. There are two: a deprecated stateless v1 (Section 6.3) and a session-bound v2 (Section 6.4). Both byte layouts and their validation rules are now normative. v1 remains the reference default, which is recorded rather than claimed as resolved.
  7. Command binding scoped. A v2 observation authenticates a SHA-256 digest of the canonical command, not the command text. It is therefore not a self-contained receipt and binds a command only relative to a verifier holding trusted request context. The abstract and Section 6.4 state this; Section 18.7 describes the intended fix.
  8. Key roles enumerated. Section 6.1 replaces the single-key description with the O-Key, R-Key, session key, K_chain, approver key, and federation key, states which are distinct, and states the one current exception: the O-Key both authenticates v1 and is the HKDF input for v2.
  9. Chain entry encoding specified. Section 6.5 defines the canonical chain-entry encoding as the compact JSON the reference implementation emits and signs, replacing the earlier description of a binary encoding that no producer implemented.
  10. Chain claims qualified. Chain integrity covers the entries presented, under K_chain. Detecting truncation or establishing the absence of a record requires an independently retained head or external anchoring (Section 18.4). Revision 05's implication that a local chain can establish absence is removed.
  11. Threat model split per key. The single "compromised verification component" row is replaced with rows for each key holder, because different holders have different forging power.
  12. Terminology. "Signed" is reserved for Ed25519; HMAC-protected records are described as "authenticated" throughout.
  13. Implementation status separated. Divergences between the specification and the reference implementation are collected in Section 19 rather than distributed through the normative text.
  14. Acknowledgements. One acknowledgement from revision 05 has been removed because the attribution could not be substantiated. A second is stated without identifying detail pending the reviewer's consent.

Work not in this revision, and expected in a subsequent one: the typed operation registry described in Section 18.7; reconciliation of Section 7 and Section 13 with the current reference deployment; and an assurance-level framework distinguishing deployments by which of these properties they actually enforce.

Author's Address

Nate Howard
Third Level IT LLC
Allen Park, MI
United States of America