NPS-Release

English 中文版

RFC Number: NPS-RFC-0004 Title: Append-only NID reputation log (Certificate Transparency for Agents) Status: Accepted (Phase 1 — entry wire format + .NET reference types landed) Author(s): Ori Lynn iamzerolin@gmail.com (LabAcacia) Shepherd: Ori Lynn (pre-1.0 fast-track per spec/cr/README.md) Created: 2026-04-21 Last-Updated: 2026-05-01 Accepted: 2026-04-26 (pre-1.0 fast-track; see spec/cr/README.md) Activated: (set when first reference log operator ships, target v1.0-alpha.4) Supersedes: none Superseded-By: none Affected Specs: NPS-3 NIP, NPS-4 NDP, spec/services/NPS-AaaS-Profile.md, spec/error-codes.md Affected SDKs: .NET, Python, TypeScript, Java, Rust, Go —

NPS-RFC-0004: Append-only NID reputation log (Certificate Transparency for Agents)

1. Summary

Define a Certificate-Transparency-style append-only log for NID behavioral incidents. Entries are signed observations (abuse reports, rate-limit violations, contract disputes, revocations) that AaaS gateways, auditors, and CAs publish against specific NIDs. A new optional NDP sub-path (/.nid/reputation?nid=...) lets any party query the aggregated record. Combined with NPS-RFC-0003 assurance levels, this gives Nodes both provenance (who is this Agent?) and track record (has it misbehaved?).

2. Motivation

Follow-up to the same 2026-04-20 review comment that drove RFC-0003. Assurance levels answer “who is this Agent?” but not “has this specific NID already caused trouble?” Concretely:

Existing domain-name reputation systems (Spamhaus, DNSBL) use private lists. The Certificate Transparency model — publicly auditable, append-only, tamper-evident, reachable by anyone — maps directly to our problem because:

3. Non-Goals

4. Detailed Design

4.1 Log Entry Format

An entry is a signed JSON object:

{
  "v": 1,
  "log_id": "nid:ed25519:<log-operator-pubkey>",
  "seq": 42817,
  "timestamp": "2026-04-21T14:30:00Z",
  "subject_nid": "nid:ed25519:<agent-pubkey>",
  "incident": "rate-limit-violation",
  "severity": "moderate",
  "window": { "start": "2026-04-21T13:00:00Z", "end": "2026-04-21T14:00:00Z" },
  "observation": {
    "requests": 45000,
    "threshold": 300,
    "endpoint_hash": "sha256:<hash-of-node-origin>"
  },
  "evidence_ref": "https://log.example.com/evidence/42817",
  "evidence_sha256": "hex-of-blob",
  "issuer_nid": "nid:ed25519:<issuer-pubkey>",
  "signature": "base64url(Ed25519(canonical-entry-without-sig))"
}

Field semantics:

Field Required Description
v yes Schema version; this RFC defines 1
log_id yes NID of the log operator appending this entry
seq yes Monotonically-increasing per-log sequence number
timestamp yes RFC 3339 UTC; log operator’s clock
subject_nid yes NID this entry is about
incident yes Enum; see §4.2
severity yes info / minor / moderate / major / critical
window no Time window the observation covers
observation no Free-form machine-readable detail, incident-type-specific
evidence_ref no URL to richer evidence blob (logs, transcript, etc.)
evidence_sha256 no SHA-256 of the evidence blob for tamper detection
issuer_nid yes NID of the party asserting the incident — MAY equal log_id
signature yes Ed25519 signature by issuer_nid’s private key over canonical form

Canonicalization for signing: JCS (RFC 8785) applied to the entry object with signature omitted. The log operator verifies the issuer signature before appending and re-signs the full entry with their own key to commit sequence number and timestamp (dual signature: issuer attests the incident, log operator attests the ordering).

4.2 Incident Vocabulary

Initial enum (extensible in follow-up RFCs):

Value Meaning
cert-revoked CA revoked the NID’s cert; subject_nid matches revocation
rate-limit-violation Sustained violation of published rate limits
tos-violation Violated AaaS gateway’s published terms
scraping-pattern Observed behavior matched scraping heuristics
payment-default CGN / fiat payment default on committed transaction
contract-dispute Contractual breach on an async NOP task, unresolved
impersonation-claim A third party claims subject_nid is impersonating them
positive-attestation Explicit positive signal (e.g., audit passed)

Unknown values MUST be preserved by log operators and returned to queriers — forward compatibility.

4.3 Log Operator Interface

4.3.1 Phase 1 — Submit and Query (current)

A log operator exposes two HTTP endpoints, discoverable via NDP:

POST /v1/log/entries        # submit a new entry (requires issuer auth)
GET  /v1/log/entries?nid=<subject_nid>&since=<seq>  # query

4.3.2 [Phase 2] — Merkle Integrity Proofs (deferred)

[Phase 2 — deferred] The following endpoints and Merkle structure are not part of the Phase 1 implementation; they are targeted for v1.0-alpha.5 per §8.1.

GET  /v1/log/sth            # signed tree head (Merkle root + seq + timestamp)
GET  /v1/log/proof?seq=<n>&tree_size=<m>  # inclusion proof

The Merkle structure mirrors RFC 9162 (CT 2.0): leaves are canonical entries; internal nodes are SHA-256 hashes; the signed tree head (STH) commits to the current root and is signed by log_id. This gives queriers cryptographic proof that an entry is included without downloading the full log.

4.4 Manifest / NDP Changes

[Phase 2 — deferred] Everything in this section is targeted for v1.0-alpha.5 per §8.1. Neither /.nid/reputation nor reputation_policy is part of the Phase 1 implementation.

NDP gains an optional well-known path for reputation discovery:

GET /.nid/reputation?nid=<nid>
    → array of log operator URLs that claim to have entries about this NID

This is a discovery hint, not a source of truth — Nodes still fetch entries from the log operators they trust.

NWM optionally declares:

# /.nwm excerpt
reputation_policy:
  required_logs: ["log:labacacia-primary", "log:some-industry-consortium"]
  reject_on:
    - { incident: "cert-revoked", severity: ">=minor" }
    - { incident: "scraping-pattern", severity: ">=major", within_days: 30 }

Nodes choosing not to check reputation simply omit the field.

4.5 STH Gossip Protocol

[Phase 3 — v1.0-alpha.5] Enables cross-log consistency verification and fork detection. OQ-1 is resolved in favour of a lightweight NPS-native variant (analogous to RFC 9162 §8.1.4 but without the HTTP-header transport and with NPS error codes).

4.5.1 Gossip Endpoint

Each log operator MUST expose:

GET /v1/log/gossip/sth

Response (JSON):

{
  "own_sth": {
    "tree_size": 42817,
    "timestamp": "2026-05-01T10:00:00Z",
    "sha256_root_hash": "hex-of-merkle-root",
    "log_id": "nid:ed25519:<log-operator-pubkey>",
    "signature": "base64url(Ed25519(jcs(own_sth without signature)))"
  },
  "peer_sths": [
    {
      "log_id": "nid:ed25519:<peer-pubkey>",
      "received_at": "2026-05-01T09:59:30Z",
      "sth": { /* same shape as own_sth */ }
    }
  ]
}

peer_sths contains the most recent validated STH received from each configured peer. Clients querying this endpoint can cross-check peers without contacting them directly.

4.5.2 Gossip Push Cycle

Log operators configured with a peers list MUST run a background gossip cycle:

  1. Fetch GET /v1/log/gossip/sth from each peer (default interval: 30 s).
  2. Verify the peer’s own_sth.signature against the peer’s log_id public key.
  3. Monotonicity check: peer’s new tree_size MUST be ≥ the last accepted tree_size for that log_id. A decrease is evidence of a fork attempt — log operators SHOULD emit a LOG-FORK-DETECTED event to local audit and cease transacting with that peer until manually reviewed.
  4. Consistency proof (SHOULD): when the peer’s tree_size increases, the operator SHOULD fetch an RFC 9162 consistency proof from GET /v1/log/proof?from=<prev_size>&to=<new_size> and verify it before accepting the new STH. Failure to verify = potential fork.
  5. Cache the accepted peer STH in memory; serve it from /gossip/sth.

4.5.3 Configuration

Log operator configuration gains an optional peers list:

{
  "peers": [
    { "log_id": "nid:ed25519:<peer>", "endpoint": "https://log2.example.com" }
  ],
  "gossip_interval_s": 30
}

gossip_interval_s defaults to 30; minimum 10; maximum 3600.

4.5.4 Error Codes (Phase 3 additions)

Error Code NPS Status Description
NIP-REPUTATION-GOSSIP-FORK NPS-SERVER-INTERNAL Cross-peer STH consistency check failed; possible fork detected
NIP-REPUTATION-GOSSIP-SIG-INVALID NPS-CLIENT-BAD-FRAME Peer STH signature verification failed

4.6 State Machines / Flows

Reputation-gated admission:

Agent                      Node                         Log Operator
  │                          │                               │
  │── HelloFrame ──────────→ │                               │
  │── IdentFrame (NID) ────→ │                               │
  │                          │ ── GET entries?nid=... ─────→ │
  │                          │ ←── 200 + entries ──────────  │
  │                          │  evaluate reject_on rules     │
  │                          │                               │
  │ ←── (accept | 403) ───── │                               │

For hot paths, Nodes SHOULD cache log results with a short TTL (default 60 s) and refresh asynchronously. A hard reject_on: cert-revoked SHOULD be checked synchronously on every connection but can be satisfied by OCSP-stapling-like pre-fetch.

4.7 Error Codes

New entries in spec/error-codes.md:

Error Code NPS Status Description
NWP-AUTH-REPUTATION-BLOCKED NPS-AUTH-FORBIDDEN Reputation policy matched a reject rule
NIP-REPUTATION-LOG-UNREACHABLE NPS-DOWNSTREAM-UNAVAILABLE Required log operator unreachable during policy evaluation
NIP-REPUTATION-ENTRY-INVALID NPS-CLIENT-BAD-FRAME Entry signature invalid or canonical form malformed

4.8 Backward Compatibility


5. Alternatives Considered

5.1 Private allowlists per Node

Each Node maintains its own blocklist/allowlist of NIDs.

5.2 CA-internal revocation only (no reputation)

Rely solely on NPS-RFC-0002’s CRL / OCSP for bad-actor signaling.

5.3 Centralized single reputation registry

A single LabAcacia-operated registry.

5.4 Do nothing


6. Drawbacks & Risks


7. Security Considerations


8. Implementation Plan

8.1 Phasing

Phase Scope Exit criterion
1 .NET reference log operator; entry format; submit + query HTTP API; no Merkle proofs yet Unit tests green; another SDK can query
2 Merkle tree + STH + inclusion proofs; NDP /.nid/reputation in all SDKs; NWM reputation_policy parsing Interop: 2 independent logs + 2 SDK clients cross-check
3 Default reputation_policy for AaaS Profile L2 tier; STH gossip between reference logs (see §4.5) Logs agree on STH within 60s of commit
4 Deprecate unsigned-entry experimental flag if any Clean

8.2 SDK Coverage Matrix

SDK Owner Status Notes
.NET Ori Lynn ✅ Phase 1+2 done; Phase 3 (gossip) in alpha.5 Reference log operator also in .NET (nps-ledger)
Python TBD Phase 1+2 pending Client only
TypeScript TBD Phase 1+2 pending
Java TBD Phase 1+2 pending
Rust TBD Phase 1+2 pending
Go TBD Phase 1+2 pending

8.3 Test Plan

  1. Entry round-trip: issuer signs → log operator appends → querier validates dual signature.
  2. [Phase 2] Inclusion proof: query entry seq=N, verify against STH at tree_size >= N+1.
  3. [Phase 2] Tamper detection: modify entry bytes in storage → STH proof fails.
  4. NWM reject_on matching: severity: ">=major" matches major and critical, not moderate.
  5. Log unreachable during policy evaluation: NIP-REPUTATION-LOG-UNREACHABLE, Node falls back per policy (fail-open or fail-closed configurable).
  6. Unknown incident value preserved and returned to querier.

8.4 Benchmarks


9. Empirical Data

None yet. Before Accepted:

Metric Baseline Proposed Delta Method
Log query latency (hot cache) N/A ≤ 20 ms Wall-clock
Entry submit throughput N/A ≥ 1000/s Load test
Merkle tree size (10M entries) N/A ≤ 1 GiB Resident set

10. Open Questions


11. Future Work


12. References


Appendix A. Revision History

Date Author Change
2026-04-21 Ori Lynn Initial draft
2026-04-26 Ori Lynn Accepted via pre-1.0 fast-track. Phase 1 spec changes landed: NPS-3 §5.1.2 Reputation Log Entry (12-field signed JSON, 8-value incident enum, 5-step severity enum, JCS dual-signature rule), error codes NIP-REPUTATION-ENTRY-INVALID / NIP-REPUTATION-LOG-UNREACHABLE / NWP-AUTH-REPUTATION-BLOCKED, new NPS-DOWNSTREAM-UNAVAILABLE status code. Phase 1 .NET reference types landed under NPS.NIP.Reputation.*. Phase 2 (Merkle tree + STH + inclusion proofs + NDP /.nid/reputation discovery + NWM reputation_policy parsing) deferred to v1.0-alpha.4 per RFC §8.1. Phase 3 (default policy in AaaS Profile L2 + STH gossip) deferred to alpha.11+.
2026-05-01 Ori Lynn Phase 3 spec landed (v1.0-alpha.5): §4.5 STH Gossip Protocol (30s push cycle, /v1/log/gossip/sth endpoint, monotonicity + consistency-proof verification, fork detection); OQ-1 resolved; two new error codes NIP-REPUTATION-GOSSIP-FORK / NIP-REPUTATION-GOSSIP-SIG-INVALID; AaaS-Profile L2 default reputation_policy added in NPS-AaaS-Profile.md. Phase 3 .NET reference implementation in nps-ledger.