<?xml version="1.0" encoding="utf-8"?>
<rfc xmlns:xi="http://www.w3.org/2001/XInclude"
     docName="draft-schrock-emilia-eye-00"
     category="exp" ipr="trust200902" submissionType="IETF"
     version="3" tocInclude="true" sortRefs="true" symRefs="true">
  <front>
    <title abbrev="EMILIA Eye Advisories">Verifiable, Scope-Bound Advisories for Authorization Posture (EMILIA Eye)</title>
    <seriesInfo name="Internet-Draft" value="draft-schrock-emilia-eye-00"/>
    <author fullname="Iman Schrock">
      <organization>EMILIA Protocol, Inc.</organization>
      <address>
        <postal>
          <country>US</country>
        </postal>
        <email>team@emiliaprotocol.ai</email>
      </address>
    </author>
    <date year="2026" month="June" day="28"/>
    <area>sec</area>
    <keyword>AI agents</keyword>
    <keyword>authorization</keyword>
    <keyword>security events</keyword>
    <keyword>continuous access evaluation</keyword>
    <keyword>advisory</keyword>
    <abstract>
      <t>This document defines the EMILIA Eye advisory: a scope-bound
      statement that an authorization posture for a named scope has
      changed (designed to be signed and offline-verifiable; the signing
      layer is specified here but is not yet present in the reference
      implementation, see Section 13.6), carrying a scope-binding hash that prevents
      the advisory from being replayed or re-targeted to a different
      scope. An Eye advisory expresses an observation-derived posture
      (clear, caution, elevated, or review_required) and a recommended
      action (none, log, step_up_auth, require_signoff, or escalate). The
      central safety invariant of this document is normative: an
      advisory MUST NEVER be the sole gate on an action. A signal may only
      TIGHTEN posture - it may cause an enforcement point to demand
      stronger authentication, human signoff, or escalation - but it can
      never itself constitute the authorization. Eye warns; an enforcement
      point verifies; an accountable human owns the decision.</t>
      <t>This work specifies the verifiability, scope-binding, and
      fail-safe advisory semantics that signal-transport frameworks leave
      undefined. It is a COMPOSABLE PROFILE: Eye advisories are carried as
      Security Event Token <xref target="RFC8417"/> payloads and MAY be
      transported over the OpenID Shared Signals Framework with Continuous
      Access Evaluation Profile (CAEP) events. It is complementary to, not
      a replacement for, SSF/CAEP (which define signal shape and
      transport, not verifiable bound fail-safe advisory semantics), and
      it composes with the EP authorization receipt
      (<xref target="I-D.schrock-ep-authorization-receipts"/>), which
      remains the artifact that actually authorizes an action. This
      document is experimental.</t>
    </abstract>
  </front>
  <middle>
    <section>
      <name>Introduction</name>
      <t>Authorization decisions are made at a moment in time, but the
      conditions that justified a decision change continuously. A device
      becomes non-compliant; a credential is reported phished; an
      anomaly-detection system observes impossible travel; a regulator
      publishes a sanctions update. Continuous access evaluation
      frameworks exist precisely to communicate such changes as they
      happen. What those frameworks deliberately do not define is (a)
      whether the recipient can verify, offline and without trusting the
      transmitter's live infrastructure, that a given signal genuinely
      originated from a trusted source; (b) how a signal is bound to one
      specific scope so it cannot be replayed against a different subject;
      and (c) what a recipient is permitted to <em>do</em> with a signal -
      in particular, whether a signal may, on its own, grant or deny
      access.</t>
      <t>This document closes those three gaps for the narrow but
      high-consequence case of authorization posture. It does so without
      reinventing the envelope or the transport. Eye advisories reuse the
      Security Event Token <xref target="RFC8417"/> as their wire
      envelope and MAY ride existing CAEP streams (<xref target="CAEP"/>,
      <xref target="SSF"/>). The contribution is three properties layered
      on top: signed offline verifiability, cryptographic scope binding,
      and a normative fail-safe rule that an advisory may only tighten
      posture and may never be the sole gate.</t>
      <t>The gaps this document closes:</t>
      <ol>
        <li><strong>The verifiability gap.</strong> A posture change
        delivered as plain JSON over an authenticated channel is only as
        trustworthy as the channel and the live database behind it. A
        relying party that caches, forwards, or audits the signal later
        cannot reconfirm its origin without re-querying the source. Eye
        signs the advisory so any holder of the issuer's public key can
        verify it offline, mirroring the offline-verifiability property of
        the EP authorization receipt.</li>
        <li><strong>The scope-binding gap.</strong> A signal that says
        "elevated risk" with a loosely attached subject reference can be
        copied and presented against a different subject. Eye binds each
        advisory to one scope via a scope-binding hash carried inside the
        signed payload, so re-targeting invalidates the signature.</li>
        <li><strong>The authority gap.</strong> Frameworks that carry
        session and posture events leave entirely to the consumer what to
        do with them, including the option of treating a signal as an
        autonomous enforcement trigger. Eye forbids that. An advisory is
        an input to a decision, never the decision.</li>
      </ol>
      <section>
        <name>Design Goals</name>
        <ul>
          <li><strong>G1 - Signed offline verifiability.</strong> An Eye
          advisory <bcp14>MUST</bcp14> be verifiable by any party holding
          the issuer's public key, with no network access to the Eye
          service, mirroring the offline-verifiability property of the EP
          authorization receipt (<xref target="offline-verification"/>).</li>
          <li><strong>G2 - Scope binding.</strong> An advisory
          <bcp14>MUST</bcp14> carry a scope-binding hash inside its signed
          payload so it cannot be replayed against, or re-targeted to, a
          scope other than the one it was issued for
          (<xref target="scope-binding"/>).</li>
          <li><strong>G3 - Never the sole gate.</strong> An advisory
          <bcp14>MUST NOT</bcp14> be the sole gate between an entity and an
          action. A signal <bcp14>MAY</bcp14> only tighten posture; it
          <bcp14>MUST NOT</bcp14> itself constitute the authorization
          (<xref target="never-sole-gate"/>). This is the central
          invariant of this specification.</li>
          <li><strong>G4 - Deterministic posture mapping.</strong> The
          mapping from advisory status to recommended action to a
          posture change at the enforcement point <bcp14>MUST</bcp14> be
          deterministic and specified, so two conformant enforcement
          points consuming the same advisory tighten posture identically
          (<xref target="posture-mapping"/>).</li>
          <li><strong>G5 - Compose, do not reinvent.</strong> Eye
          advisories <bcp14>MUST</bcp14> be expressible as a Security Event
          Token <xref target="RFC8417"/> payload, and SSF/CAEP transport
          is <bcp14>OPTIONAL</bcp14>. Eye defines the verifiable, bound,
          fail-safe layer; it does not define a new event envelope or a
          new delivery protocol (<xref target="set-envelope"/>,
          <xref target="related-work"/>).</li>
          <li><strong>G6 - Append-only honesty.</strong> Advisories and
          their underlying observations are append-only; a status change is
          a new advisory that supersedes its predecessor, never a mutation
          of an existing one (<xref target="lifecycle"/>).</li>
        </ul>
      </section>
      <section anchor="scope">
        <name>Scope</name>
        <t>This document defines the advisory artifact, its scope binding,
        its signature, the observation signal registry, and the
        deterministic status-to-posture mapping that bridges into an
        enforcement point. It does not define a policy language, an
        anomaly-detection method, the transport stream's management and
        verification (which SSF provides), or the authorization artifact
        itself (which the EP authorization receipt provides). Eye produces
        advice; it never produces an authorization.</t>
        <t>Implementation status is honestly noted in
        <xref target="implementation-status"/>: the signing, SET-envelope,
        and CAEP-transport layers specified here are experimental and not
        yet present in the reference implementation, which currently
        computes scope-binding hashes but does not asymmetrically sign
        advisories.</t>
      </section>
    </section>
    <section anchor="terminology">
      <name>Terminology</name>
      <t>The key words "<bcp14>MUST</bcp14>", "<bcp14>MUST NOT</bcp14>",
      "<bcp14>REQUIRED</bcp14>", "<bcp14>SHALL</bcp14>",
      "<bcp14>SHALL NOT</bcp14>", "<bcp14>SHOULD</bcp14>",
      "<bcp14>SHOULD NOT</bcp14>", "<bcp14>RECOMMENDED</bcp14>",
      "<bcp14>NOT RECOMMENDED</bcp14>", "<bcp14>MAY</bcp14>", and
      "<bcp14>OPTIONAL</bcp14>" in this document are to be interpreted as
      described in BCP 14 <xref target="RFC2119"/> <xref target="RFC8174"/>
      when, and only when, they appear in all capitals, as shown here.</t>
      <t><strong>Eye / Eye Service.</strong> The service that ingests
      observations from trusted sources and issues advisories. The Eye
      service is an advice-producing party; under this protocol it is
      never an authorizing party.</t>
      <t><strong>Observation.</strong> A single, source-attributed,
      scope-bound signal that some condition relevant to authorization
      posture has been detected (e.g., a device became non-compliant).
      Observations are the inputs from which advisories are derived
      (<xref target="observation"/>).</t>
      <t><strong>Signal Code.</strong> A registered identifier naming the
      kind of an observation (<xref target="signal-registry"/>). Signal
      codes are the vocabulary in which sources speak to Eye.</t>
      <t><strong>Advisory.</strong> The terminal Eye artifact: a signed,
      scope-bound statement of posture (status) and recommendation
      (recommended_action) for one scope, derived from one or more
      contributing observations (<xref target="advisory"/>).</t>
      <t><strong>Scope.</strong> The object an advisory is about,
      identified by a scope_type (entity, action, resource, or
      environment) and a scope_ref. An advisory speaks only about its
      scope.</t>
      <t><strong>Scope-Binding Hash.</strong> A SHA-256 digest over the
      canonical scope identity, carried inside the signed advisory, that
      binds the advisory to exactly one scope
      (<xref target="scope-binding"/>).</t>
      <t><strong>Enforcement Point.</strong> The component that consumes an
      advisory and may tighten authorization posture in response - for
      example, by raising the required authentication level or demanding
      human signoff. In the EP family this is the verifying executor of
      the EP authorization receipt
      (<xref target="I-D.schrock-ep-authorization-receipts"/>). The
      enforcement point - not Eye - owns the decision.</t>
      <t><strong>Posture.</strong> The strength of the checks an
      enforcement point applies to a prospective action. Tightening
      posture means requiring more (stronger auth, signoff, escalation);
      it never means granting access that policy would otherwise deny.</t>
    </section>
    <section anchor="never-sole-gate">
      <name>The Fail-Safe Invariant: An Advisory Is Never the Sole Gate</name>
      <t>This is the architectural invariant that governs every other
      design choice in this document. It is stated first, before the wire
      format, because the wire format exists to serve it.</t>
      <t><strong>Eye warns. The enforcement point verifies. An accountable
      human owns.</strong> An Eye advisory <bcp14>MUST NOT</bcp14> be the
      sole gate between an entity and an action. If an advisory is ever the
      only thing standing between an entity and an action, the integration
      is non-conformant with this specification.</t>
      <t>Two corollaries make the invariant precise and testable:</t>
      <ol>
        <li><strong>Tighten-only (one-directional).</strong> A signal
        <bcp14>MAY</bcp14> only cause an enforcement point to demand
        <em>more</em> than it otherwise would (stronger authentication,
        human signoff, escalation, or a hard stop pending review). A
        signal <bcp14>MUST NOT</bcp14> cause an enforcement point to demand
        <em>less</em>, and a "clear" advisory <bcp14>MUST NOT</bcp14> be
        treated as an authorization, an approval, or a grant. The absence
        of a warning is not a permission.</li>
        <li><strong>Necessary-not-sufficient.</strong> An action that the
        underlying authorization policy would deny <bcp14>MUST</bcp14>
        remain denied regardless of any advisory. An advisory can subtract
        nothing from the set of conditions an action must satisfy; it can
        only add conditions. The authorization decision is owned by the
        enforcement point and, where the policy requires it, an accountable
        human (the EP signoff); the advisory is one input among those the
        decision considers.</li>
      </ol>
      <t>Rationale. Signals are derived from heuristics, third-party feeds,
      and detectors that fail in both directions. A false negative (no
      signal) must never be readable as an affirmative grant, and a feed
      outage must never widen access. Confining advisories to a
      tighten-only role means the worst an advisory error can do is
      over-restrict - a fail-safe, recoverable outcome - never
      over-authorize. A conformant enforcement point that receives no
      advisory, an expired advisory, or an unverifiable advisory
      <bcp14>MUST</bcp14> behave as though posture were unchanged by Eye
      and fall back to its baseline policy; it <bcp14>MUST NOT</bcp14> fail
      open.</t>
    </section>
    <section anchor="observation">
      <name>The Observation</name>
      <t>An observation is a single source-attributed signal about one
      scope. It is the input to advisory derivation and is itself
      scope-bound.</t>
      <sourcecode type="json"><![CDATA[
{
  "ep_version": "1.0",
  "observation_id": "ep:eye:obs:01J...",
  "source_id": "ep:source:edr-crowdstrike",
  "source_type": "infrastructure",
  "scope_type": "entity",
  "scope_ref": "ep:entity:agent-recon-7",
  "scope_binding_hash": "sha256:7a3f...",
  "signal_code": "device.compliance.failed",
  "severity": "high",
  "evidence_ref": "ep:evidence:edr/8841",
  "detail": { "policy": "disk-encryption", "host": "wsx-12" },
  "observed_at": "2026-06-13T17:21:04Z",
  "expires_at": "2026-06-13T18:21:04Z",
  "created_at": "2026-06-13T17:21:05Z"
}
]]></sourcecode>
      <t>Rules:</t>
      <ul>
        <li><tt>source_id</tt> <bcp14>MUST</bcp14> identify a source
        authenticated to the Eye service. <tt>source_type</tt>
        <bcp14>MUST</bcp14> be one of <tt>internal</tt>,
        <tt>partner</tt>, <tt>regulatory</tt>, or
        <tt>infrastructure</tt>.</li>
        <li><tt>signal_code</tt> <bcp14>MUST</bcp14> be drawn from the
        signal registry (<xref target="signal-registry"/>). An observation
        carrying an unregistered signal_code <bcp14>MUST NOT</bcp14>
        contribute to an advisory's posture; it <bcp14>MAY</bcp14> be
        logged.</li>
        <li><tt>severity</tt> <bcp14>MUST</bcp14> be one of
        <tt>info</tt>, <tt>low</tt>, <tt>medium</tt>, <tt>high</tt>, or
        <tt>critical</tt>.</li>
        <li><tt>scope_binding_hash</tt> <bcp14>MUST</bcp14> be computed as
        in <xref target="scope-binding"/> over this observation's
        <tt>scope_type</tt> and <tt>scope_ref</tt>.</li>
        <li><tt>expires_at</tt> <bcp14>MUST</bcp14> be present.
        Observations are time-bounded inputs; an expired observation
        <bcp14>MUST NOT</bcp14> contribute to a newly issued advisory.</li>
      </ul>
    </section>
    <section anchor="scope-binding">
      <name>Scope Binding</name>
      <t>Scope binding prevents an advisory (or observation) from being
      copied and presented against a scope other than the one it was
      issued for. The <strong>scope-binding hash</strong> is the SHA-256
      digest of the JSON Canonicalization Scheme <xref target="RFC8785"/>
      serialization of the scope identity object:</t>
      <sourcecode type="json"><![CDATA[
{
  "ep_version": "1.0",
  "binding_type": "ep.eye.scope.v1",
  "scope_type": "entity",
  "scope_ref": "ep:entity:agent-recon-7"
}
]]></sourcecode>
      <t>Rules:</t>
      <ul>
        <li>The scope-binding hash <bcp14>MUST</bcp14> be carried inside
        the signed advisory payload (<xref target="advisory"/>), so that
        altering either <tt>scope_type</tt> or <tt>scope_ref</tt>
        invalidates the signature.</li>
        <li>An enforcement point applying an advisory to a candidate action
        <bcp14>MUST</bcp14> recompute the scope-binding hash from the
        scope it is actually evaluating and <bcp14>MUST</bcp14> reject the
        advisory if the recomputed hash does not equal the advisory's
        <tt>scope_binding_hash</tt>. This is the check that defeats
        re-targeting.</li>
        <li>The scope-binding hash is a binding, not a secret: it commits
        to the scope but reveals only what the scope identity already
        reveals. Where <tt>scope_ref</tt> is sensitive, deployments
        <bcp14>MAY</bcp14> use a salted scope reference, provided the
        enforcement point can recompute it for the scope under
        evaluation.</li>
      </ul>
    </section>
    <section anchor="advisory">
      <name>The Advisory</name>
      <t>An advisory is the scope-bound statement (signed in the Eye-Verifiable
      class; see Section 13.6) Eye issues for a scope. It is the artifact a relying party verifies and an enforcement
      point consumes.</t>
      <section anchor="advisory-payload">
        <name>Advisory Payload</name>
        <sourcecode type="json"><![CDATA[
{
  "ep_version": "1.0",
  "advisory_type": "ep.eye.advisory.v1",
  "advisory_id": "ep:eye:adv:01J...",
  "issuer": "ep:eye:acme-primary",
  "scope_type": "entity",
  "scope_ref": "ep:entity:agent-recon-7",
  "scope_binding_hash": "sha256:7a3f...",
  "status": "elevated",
  "reason_codes": ["device.compliance.failed",
                   "auth.anomaly.impossible_travel"],
  "contributing_observations": ["ep:eye:obs:01J...",
                                "ep:eye:obs:02K..."],
  "recommended_action": "step_up_auth",
  "detail": { "highest_severity": "high" },
  "issued_at": "2026-06-13T17:21:06Z",
  "expires_at": "2026-06-13T17:36:06Z",
  "superseded_by": null
}
]]></sourcecode>
        <t>Rules:</t>
        <ul>
          <li><tt>status</tt> <bcp14>MUST</bcp14> be one of
          <tt>clear</tt>, <tt>caution</tt>, <tt>elevated</tt>, or
          <tt>review_required</tt> (<xref target="status-enum"/>).</li>
          <li><tt>recommended_action</tt> <bcp14>MUST</bcp14> be one of
          <tt>none</tt>, <tt>log</tt>, <tt>step_up_auth</tt>,
          <tt>require_signoff</tt>, or <tt>escalate</tt>
          (<xref target="action-enum"/>), and <bcp14>MUST</bcp14> be the
          value the deterministic mapping (<xref target="posture-mapping"/>)
          assigns to <tt>status</tt>.</li>
          <li><tt>reason_codes</tt> <bcp14>MUST NOT</bcp14> be empty for
          any non-<tt>clear</tt> status. Each entry <bcp14>SHOULD</bcp14>
          be a registered <tt>signal_code</tt> drawn from the contributing
          observations.</li>
          <li><tt>contributing_observations</tt> <bcp14>MUST</bcp14> list
          the <tt>observation_id</tt> of every observation that influenced
          <tt>status</tt>. For a <tt>clear</tt> advisory it
          <bcp14>MAY</bcp14> be empty.</li>
          <li><tt>scope_binding_hash</tt> <bcp14>MUST</bcp14> equal the
          hash of this advisory's scope (<xref target="scope-binding"/>).</li>
          <li><tt>expires_at</tt> <bcp14>MUST</bcp14> be present. An
          enforcement point <bcp14>MUST</bcp14> treat an advisory whose
          <tt>expires_at</tt> is in the past as absent and fall back to
          baseline posture (<xref target="never-sole-gate"/>); it
          <bcp14>MUST NOT</bcp14> infer a grant from expiry.</li>
        </ul>
      </section>
      <section anchor="advisory-signature">
        <name>Advisory Signature and Offline Verification</name>
        <t>The advisory is signed by the Eye operator's issuing key. The
        signed document mirrors the structure of the EP authorization
        receipt's evidence document so that the same verifier core can
        check both:</t>
        <sourcecode type="json"><![CDATA[
{
  "@version": "EP-EYE-ADVISORY-v1",
  "payload": { "...": "Advisory Payload above" },
  "signature": {
    "algorithm": "Ed25519",
    "signer": "ep:eye:acme-primary",
    "key_id": "ep-eye-signing-key-1",
    "value": "b64u:..."
  }
}
]]></sourcecode>
        <t>The signature <bcp14>MUST</bcp14> be computed over the JCS
        <xref target="RFC8785"/> canonicalization of the
        <tt>payload</tt> object, so that a verifier re-canonicalizes and
        re-derives the exact bytes the signature covers. Ed25519
        <xref target="RFC8032"/> is the <bcp14>RECOMMENDED</bcp14>
        algorithm.</t>
        <section anchor="offline-verification">
          <name>Offline Verification Algorithm</name>
          <t>A verifier with (the signed advisory, the issuer's trusted
          public key, and the scope it is evaluating) and <strong>no
          network access</strong> <bcp14>MUST</bcp14> be able to establish
          all of the following:</t>
          <ol>
            <li>Verify the signature over <tt>canonicalize(payload)</tt>
            against the issuer's public key. The signer key
            <bcp14>MUST</bcp14> be pinned to a source-independent trust
            root, never trusted solely because it accompanied the
            advisory.</li>
            <li>Recompute the scope-binding hash from the scope under
            evaluation; confirm it equals <tt>payload.scope_binding_hash</tt>
            (<xref target="scope-binding"/>). A mismatch
            <bcp14>MUST</bcp14> cause rejection: the advisory is for a
            different scope.</li>
            <li>Confirm that <tt>recommended_action</tt> is the value the
            deterministic mapping (<xref target="posture-mapping"/>)
            assigns to <tt>status</tt>; a divergent advisory
            <bcp14>MUST</bcp14> be rejected as malformed.</li>
            <li>Confirm the current time is at or before
            <tt>expires_at</tt>. An expired advisory is treated as absent
            (<xref target="never-sole-gate"/>).</li>
          </ol>
          <t>As with the EP authorization receipt, offline verification
          establishes authenticity and scope binding as of issuance, not
          currency. Two properties are explicitly NOT established offline:
          (a) supersession - a later advisory may have replaced this one
          via <tt>superseded_by</tt>, which a cached holder cannot see; and
          (b) issuer-key revocation after issuance. A relying party with
          freshness requirements <bcp14>MUST</bcp14> additionally consult
          the Eye service or its published checkpoint online. Crucially,
          because advisories are tighten-only (<xref target="never-sole-gate"/>),
          a stale advisory can only over-restrict, never over-authorize;
          the offline guarantee is therefore fail-safe. Implementations
          <bcp14>MUST NOT</bcp14> describe offline verification as
          establishing that an advisory is "currently the latest."</t>
        </section>
      </section>
    </section>
    <section anchor="signal-registry">
      <name>The Signal Registry</name>
      <t>The signal registry is the controlled vocabulary of observation
      signal codes. Each entry names a detectable condition, the
      <tt>scope_type</tt> it applies to, and the maximum status it
      <bcp14>MAY</bcp14> justify. The registry below is the experimental
      starting set; it is expected to grow. A deployment
      <bcp14>MAY</bcp14> register additional codes in a private namespace
      (prefix <tt>x-</tt>) but <bcp14>MUST NOT</bcp14> redefine a
      registered code.</t>
      <table>
        <thead>
          <tr>
            <th>signal_code</th>
            <th>scope</th>
            <th>meaning</th>
            <th>max</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td><tt>device.compliance.failed</tt></td>
            <td>entity</td>
            <td>endpoint posture check failed</td>
            <td>elev.</td>
          </tr>
          <tr>
            <td><tt>credential.phish.reported</tt></td>
            <td>entity</td>
            <td>credential reported phished</td>
            <td>review</td>
          </tr>
          <tr>
            <td><tt>auth.anomaly.impossible_travel</tt></td>
            <td>entity</td>
            <td>geographically impossible logins</td>
            <td>elev.</td>
          </tr>
          <tr>
            <td><tt>session.assurance.downgraded</tt></td>
            <td>entity</td>
            <td>assurance dropped mid-session</td>
            <td>elev.</td>
          </tr>
          <tr>
            <td><tt>resource.sensitivity.raised</tt></td>
            <td>resource</td>
            <td>resource reclassified higher</td>
            <td>caut.</td>
          </tr>
          <tr>
            <td><tt>action.velocity.anomaly</tt></td>
            <td>action</td>
            <td>rate/pattern deviates baseline</td>
            <td>elev.</td>
          </tr>
          <tr>
            <td><tt>counterparty.sanctions.hit</tt></td>
            <td>action</td>
            <td>counterparty on sanctions list</td>
            <td>review</td>
          </tr>
          <tr>
            <td><tt>environment.threat.elevated</tt></td>
            <td>env.</td>
            <td>environment threat level raised</td>
            <td>caut.</td>
          </tr>
        </tbody>
      </table>
      <t>The "max" column abbreviates the maximum status a single signal of
      that code may justify (caut. = caution, elev. = elevated, review =
      review_required). A "max status" caps how far a single signal of that code may move
      posture; it does not floor it. Eye derives an advisory's
      <tt>status</tt> from the contributing observations by taking the
      highest status any non-expired contributing observation justifies,
      bounded by each signal's max status. The full derivation is
      deployment policy and out of scope; what is normative is that the
      resulting <tt>status</tt> deterministically yields
      <tt>recommended_action</tt> per <xref target="posture-mapping"/>.</t>
    </section>
    <section anchor="posture-mapping">
      <name>Deterministic Status-to-Posture Mapping</name>
      <t>This section is the bridge from an Eye advisory into an
      enforcement point - and, in the EP family, into EP verification. The
      mapping is normative and deterministic: every conformant enforcement
      point consuming the same advisory <bcp14>MUST</bcp14> tighten posture
      identically.</t>
      <section anchor="status-enum">
        <name>Status Semantics</name>
        <ul>
          <li><strong>clear.</strong> No active observations; no signals
          of concern. <em>Not</em> an authorization - see
          <xref target="never-sole-gate"/>.</li>
          <li><strong>caution.</strong> Low-severity observations exist;
          context has changed but does not suggest immediate risk.</li>
          <li><strong>elevated.</strong> Medium-to-high severity
          observations exist; context suggests increased risk warranting
          additional verification.</li>
          <li><strong>review_required.</strong> Critical observations
          exist; context suggests the action should not proceed without
          explicit human review.</li>
        </ul>
      </section>
      <section anchor="action-enum">
        <name>Recommended Action Semantics</name>
        <ul>
          <li><strong>none.</strong> No additional action required by
          Eye.</li>
          <li><strong>log.</strong> The enforcement point may record the
          advisory.</li>
          <li><strong>step_up_auth.</strong> Raise the required
          authentication level for the action.</li>
          <li><strong>require_signoff.</strong> Require accountable human
          signoff before the action proceeds.</li>
          <li><strong>escalate.</strong> Escalate for manual review or
          handling; the action does not proceed on Eye's recommendation
          alone.</li>
        </ul>
      </section>
      <section anchor="mapping-table">
        <name>The Mapping</name>
        <table>
          <thead>
            <tr>
              <th>advisory.status</th>
              <th>recommended_action</th>
              <th>Posture change at the enforcement point</th>
            </tr>
          </thead>
          <tbody>
            <tr>
              <td><tt>clear</tt></td>
              <td><tt>none</tt></td>
              <td>No change. Baseline policy applies unchanged. NOT a grant.</td>
            </tr>
            <tr>
              <td><tt>caution</tt></td>
              <td><tt>log</tt></td>
              <td>Record the advisory; baseline policy otherwise unchanged.</td>
            </tr>
            <tr>
              <td><tt>elevated</tt></td>
              <td><tt>step_up_auth</tt></td>
              <td>Require stronger authentication than baseline before proceeding.</td>
            </tr>
            <tr>
              <td><tt>review_required</tt></td>
              <td><tt>require_signoff</tt> or <tt>escalate</tt></td>
              <td>Require accountable human signoff, or escalate; never proceed on Eye alone.</td>
            </tr>
          </tbody>
        </table>
        <t>Every entry in the "posture change" column is monotonic in the
        tightening direction (<xref target="never-sole-gate"/>): each
        named change adds a requirement and removes none.</t>
      </section>
      <section anchor="ep-bridge">
        <name>Bridge into the Enforcement-Point Profile and EP Verification</name>
        <t>In an EP deployment, the enforcement point is the verifying
        executor of the EP authorization receipt
        (<xref target="I-D.schrock-ep-authorization-receipts"/>). The
        advisory tightens, but never replaces, EP verification:</t>
        <ul>
          <li>On <tt>step_up_auth</tt>, the enforcement point
          <bcp14>SHOULD</bcp14> require an EP signoff key class
          corresponding to stronger assurance (e.g., a device-bound
          WebAuthn key) for the action's required approvals, raising the
          bar above the policy baseline.</li>
          <li>On <tt>require_signoff</tt>, the enforcement point
          <bcp14>MUST</bcp14> require a valid EP authorization receipt with
          accountable human signoff before the action executes, even if the
          baseline policy would not otherwise have required one. The
          advisory adds the signoff requirement; the EP receipt - not the
          advisory - authorizes the action.</li>
          <li>On <tt>escalate</tt>, the enforcement point
          <bcp14>MUST NOT</bcp14> allow the action to proceed on the
          advisory alone; it routes the action to human review, whose
          outcome (if it proceeds) is itself an EP signoff.</li>
        </ul>
        <t>In every case the EP authorization receipt remains the artifact
        that authorizes; the advisory only determines how high the bar is
        set. An enforcement point <bcp14>MUST NOT</bcp14> synthesize, or
        treat the advisory as, an EP authorization receipt
        (<xref target="never-sole-gate"/>).</t>
      </section>
    </section>
    <section anchor="lifecycle">
      <name>Advisory Lifecycle and Supersession</name>
      <t>Advisories and observations are append-only. A posture change for
      a scope is expressed by issuing a new advisory and setting the prior
      advisory's <tt>superseded_by</tt> to the new
      <tt>advisory_id</tt>; an existing advisory's signed payload is never
      mutated. Because the signed payload is immutable, the superseding
      link is metadata visible only to a party querying the Eye service
      online; an offline holder sees only the advisory it holds, which is
      sound because tighten-only semantics make a stale advisory fail-safe
      (<xref target="offline-verification"/>). Deployments
      <bcp14>SHOULD</bcp14> keep advisory <tt>expires_at</tt> windows short
      to bound the staleness an offline holder can carry.</t>
    </section>
    <section anchor="set-envelope">
      <name>Composition: SET Envelope and Optional SSF/CAEP Transport</name>
      <t>Eye does not define a new event envelope or a new delivery
      protocol. It composes over existing standards and adds only the
      verifiability, scope-binding, and fail-safe semantics those
      standards leave undefined.</t>
      <section anchor="set-mapping">
        <name>Security Event Token Envelope</name>
        <t>An Eye advisory <bcp14>MUST</bcp14> be expressible as a Security
        Event Token <xref target="RFC8417"/>. The signed advisory document
        (<xref target="advisory-signature"/>) is carried as the
        event-specific payload of a SET event whose event-type URI is
        <tt>https://schemas.emiliaprotocol.ai/eye/advisory</tt>:</t>
        <sourcecode type="json"><![CDATA[
{
  "iss": "https://eye.acme.example",
  "iat": 1781000466,
  "jti": "ep:eye:adv:01J...",
  "aud": "https://ep.acme.example",
  "sub_id": { "format": "opaque",
              "id": "ep:entity:agent-recon-7" },
  "events": {
    "https://schemas.emiliaprotocol.ai/eye/advisory": {
      "@version": "EP-EYE-ADVISORY-v1",
      "payload": { "...": "Advisory Payload" },
      "signature": { "...": "advisory signature" }
    }
  }
}
]]></sourcecode>
        <t>The SET's own JWT-level protections (and any JWS signature over
        the SET) are complementary to, and do not replace, the advisory's
        embedded signature. The embedded signature is what survives
        re-transmission and caching and provides the offline verifiability
        of <xref target="offline-verification"/>; the SET envelope
        provides standards-aligned event framing. Per
        <xref target="RFC8417"/>, the SET does not define how the event is
        delivered.</t>
      </section>
      <section anchor="ssf-transport">
        <name>Optional SSF/CAEP Transport</name>
        <t>Eye advisories <bcp14>MAY</bcp14> be transported over the
        OpenID Shared Signals Framework <xref target="SSF"/> as
        SET-formatted events on a stream between an Eye service
        (Transmitter) and an enforcement point (Receiver), alongside
        Continuous Access Evaluation Profile <xref target="CAEP"/> events.
        CAEP defines event types describing session and posture changes;
        Eye does not duplicate them. Where a CAEP event (e.g., a device
        compliance change) is the upstream cause of an observation, the
        observation's <tt>evidence_ref</tt> <bcp14>MAY</bcp14> reference
        that CAEP event.</t>
        <t>SSF/CAEP define the shape and transport of signals. They do not
        define whether a signal is verifiable offline, how it is bound to a
        scope, or what a recipient may do with it - in particular, they do
        not forbid treating a signal as an autonomous enforcement trigger.
        Eye supplies exactly those missing properties and is therefore the
        verifiable, bound, fail-safe advisory layer on top of SSF/CAEP,
        <strong>not</strong> a competitor to them. A deployment that uses
        SSF/CAEP transport <bcp14>MUST</bcp14> still enforce the fail-safe
        invariant (<xref target="never-sole-gate"/>) and scope-binding
        check (<xref target="scope-binding"/>) on every advisory it
        receives.</t>
      </section>
    </section>
    <section anchor="conformance">
      <name>Conformance Classes</name>
      <t>Honesty about deployment topology is a protocol feature, as in the
      EP authorization receipt. An implementation
      <bcp14>MUST</bcp14> declare its class, and claims
      <bcp14>MUST NOT</bcp14> state a stronger class than deployed.</t>
      <t><strong>Eye-Verifiable (STRONG).</strong> Advisories are
      asymmetrically signed (<xref target="advisory-signature"/>) and
      verifiable offline; enforcement points perform the full offline
      verification algorithm and enforce both the scope-binding check and
      the fail-safe invariant.</t>
      <t><strong>Eye-Bound (STANDARD).</strong> Advisories carry and
      enforce the scope-binding hash and the fail-safe invariant, but are
      not asymmetrically signed; integrity rests on transport
      authentication and the out-of-band-known hash. This matches the
      current reference implementation
      (<xref target="implementation-status"/>).</t>
      <t><strong>Eye-Advisory Only (BASIC).</strong> Advisories are
      consumed as policy input with the fail-safe invariant enforced, but
      without offline verification or scope-binding enforcement. No
      verifiability claim is made.</t>
      <t>The fail-safe invariant (<xref target="never-sole-gate"/>) is
      <bcp14>REQUIRED</bcp14> of every conformance class, including BASIC.
      It is the one property an Eye deployment may never omit.</t>
    </section>
    <section anchor="related-work">
      <name>Relationship to Other Work</name>
      <t><strong>Security Event Token (<xref target="RFC8417"/>)</strong>
      defines the JWT-based event envelope Eye reuses as its wire
      container. Eye adds verifiable, scope-bound, fail-safe advisory
      semantics inside that envelope; it does not define a new envelope.</t>
      <t><strong>OpenID SSF / CAEP (<xref target="SSF"/>,
      <xref target="CAEP"/>)</strong> define asynchronous SET delivery and
      a fixed set of session/posture event types. They standardize the
      shape and transport of signals but deliberately leave undefined the
      verifiable scope-bound advisory and the "never the sole gate"
      invariant. Eye supplies those and rides SSF/CAEP as
      <bcp14>OPTIONAL</bcp14> transport - complementary, not competing.</t>
      <t><strong>EP authorization receipt
      (<xref target="I-D.schrock-ep-authorization-receipts"/>)</strong> is
      the artifact that actually authorizes an action, binding an
      accountable human signoff to one exact action and verifiable offline.
      Eye composes with it: an advisory tightens the bar an EP receipt must
      clear (<xref target="ep-bridge"/>) but never substitutes for the
      receipt. Eye warns; the EP receipt authorizes.</t>
      <t><strong>AuthZEN and policy engines (OPA, Cedar).</strong>
      Authorization-decision semantics live in a policy decision point
      consulted by a policy enforcement point. Eye does not define a policy
      language; an Eye-aware enforcement point acts as (or alongside) a
      policy enforcement point and treats the advisory as additional
      context that can only tighten the decision, never as the decision
      itself.</t>
    </section>
    <section>
      <name>Security Considerations</name>
      <section anchor="fail-open">
        <name>Fail-Safe, Never Fail-Open</name>
        <t>The defining risk this document guards against is an advisory
        being read as a permission. The fail-safe invariant
        (<xref target="never-sole-gate"/>) is the mitigation, stated as a
        normative <bcp14>MUST</bcp14>: a missing, expired, unverifiable, or
        <tt>clear</tt> advisory <bcp14>MUST</bcp14> leave baseline policy
        in force and <bcp14>MUST NOT</bcp14> be treated as a grant. An
        implementation that lets an Eye outage widen access has inverted
        the protocol's central guarantee. Because advisories are
        tighten-only, every other failure mode in this section degrades
        toward over-restriction, which is recoverable, rather than toward
        over-authorization, which is not.</t>
      </section>
      <section anchor="false-signals">
        <name>False and Adversarial Signals</name>
        <t>A source may emit a false observation - through error, feed
        poisoning, or compromise of the source. Under tighten-only
        semantics the worst direct effect is denial of service by
        over-restriction (spurious step-up or signoff), not unauthorized
        access. Deployments <bcp14>SHOULD</bcp14> rate-limit and
        reputation-weight sources, <bcp14>SHOULD</bcp14> bound how far any
        single signal_code can move posture (the registry's max status,
        <xref target="signal-registry"/>), and <bcp14>SHOULD NOT</bcp14>
        let a single source unilaterally reach <tt>review_required</tt>
        for high-value scopes without corroboration. An adversary who can
        <em>suppress</em> true signals gains only the absence of
        tightening, which - by the fail-safe invariant - returns the
        system to its baseline policy, not to an open state.</t>
      </section>
      <section anchor="scope-replay">
        <name>Scope Replay and Re-Targeting</name>
        <t>Without scope binding, an advisory observed for one scope could
        be replayed against another. The scope-binding hash
        (<xref target="scope-binding"/>), carried inside the signed
        payload and recomputed by the enforcement point against the scope
        it is actually evaluating, defeats this: a re-targeted advisory
        fails the hash comparison. Note that re-targeting an advisory can
        only ever <em>add</em> tightening to the wrong scope (a fail-safe
        denial), never authorize it; scope binding nonetheless
        <bcp14>MUST</bcp14> be enforced to prevent denial-of-service by
        misdirected advisories.</t>
      </section>
      <section anchor="signer-pinning">
        <name>Issuer-Key Trust and Pinning</name>
        <t>Offline verification (<xref target="offline-verification"/>) is
        only as sound as the trust root for the issuer's key. A verifier
        <bcp14>MUST</bcp14> pin the issuer key to a source-independent
        trust root and <bcp14>MUST NOT</bcp14> trust a key solely because
        it accompanied the advisory or arrived over the same channel.
        Issuer-key revocation after issuance is not detectable offline; a
        relying party with revocation requirements
        <bcp14>MUST</bcp14> consult a current issuer-key checkpoint
        online. As elsewhere, the tighten-only property bounds the damage:
        a forged advisory from an untrusted key, if (wrongly) accepted, can
        only over-restrict.</t>
      </section>
      <section anchor="staleness">
        <name>Staleness and Supersession</name>
        <t>An offline holder cannot observe that a newer advisory has
        superseded the one it holds (<xref target="lifecycle"/>). For a
        <tt>clear</tt> or low advisory this could mean acting on a posture
        milder than the current truth - but because a missing or milder
        advisory only fails to tighten (it never grants), the enforcement
        point's baseline policy still applies. Relying parties that require
        currency <bcp14>MUST</bcp14> query online and
        <bcp14>SHOULD</bcp14> keep <tt>expires_at</tt> windows short
        (<xref target="lifecycle"/>). Implementations
        <bcp14>MUST NOT</bcp14> describe a cached advisory as the
        authoritative current posture.</t>
      </section>
      <section anchor="implementation-status">
        <name>Implementation Status and What Is Not Yet Built</name>
        <t>This is an experimental specification. The reference
        implementation currently computes scope-binding and advisory hashes
        over canonical fields and enforces append-only storage and the
        fail-safe invariant, but it does <em>not</em> yet asymmetrically
        sign advisories, does not yet emit the SET envelope
        (<xref target="set-envelope"/>), and does not yet transport over
        SSF/CAEP. In the conformance terms of
        <xref target="conformance"/>, the current implementation is
        Eye-Bound (STANDARD), not Eye-Verifiable (STRONG). The signing,
        SET-envelope, and CAEP-transport sections describe target
        behavior, not deployed behavior, and are marked experimental
        accordingly. No claim of production deployment, adoption, or
        third-party relying-party use is made.</t>
      </section>
      <section anchor="boundary">
        <name>What Eye Does and Does Not Decide</name>
        <t>Eye contributes context to an authorization decision; it never
        makes one. The strongest accurate claim is: "an Eye advisory can
        cause an action to require more before it proceeds." It cannot
        cause an action to require less, and it cannot, by itself, cause an
        action to proceed. Implementations <bcp14>MUST NOT</bcp14>
        represent Eye as an access-control system, an authorization service,
        or a gate; it is an advisory layer whose role is fixed by
        <xref target="never-sole-gate"/>.</t>
      </section>
    </section>
    <section>
      <name>IANA Considerations</name>
      <t>This document has no IANA actions. A future version may register
      the SET event-type URI
      <tt>https://schemas.emiliaprotocol.ai/eye/advisory</tt> and the
      <tt>application/ep-eye-advisory+json</tt> media type.</t>
    </section>
  </middle>
  <back>
    <references>
      <name>Normative References</name>
      <xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.2119.xml"/>
      <xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.8174.xml"/>
      <xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.8785.xml"/>
      <xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.8417.xml"/>
      <xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.8032.xml"/>
    </references>
    <references>
      <name>Informative References</name>
      <xi:include href="https://bib.ietf.org/public/rfc/bibxml3/reference.I-D.schrock-ep-authorization-receipts.xml"/>
      <reference anchor="SSF" target="https://openid.net/specs/openid-sharedsignals-framework-1_0.html">
        <front>
          <title>OpenID Shared Signals Framework 1.0</title>
          <author><organization>OpenID Foundation</organization></author>
          <date year="2025"/>
        </front>
      </reference>
      <reference anchor="CAEP" target="https://openid.net/specs/openid-caep-1_0-final.html">
        <front>
          <title>OpenID Continuous Access Evaluation Profile 1.0</title>
          <author><organization>OpenID Foundation</organization></author>
          <date year="2025" month="August"/>
        </front>
      </reference>
    </references>
  </back>
</rfc>
