Protocol overview
The open, typed contract between Numinance and everything it touches: package map, design principles, and how the protocol is versioned and kept compatible.
The brain is private. The contract is open. Everything a plugin, a policy file or a verifier needs to interoperate is protobuf you can read, lint and generate from.
When to use this section
Read the protocol pages when you build a plugin, write a provider for a port, generate a client, or check exactly what a configuration field or enum value means. If you are new to Numinance, the concepts section explains why the contract has this shape; this section is the reference.
Design principles
| Principle | What it means in the contract |
|---|---|
| Authority outside the model | Every RPC carries an action annotation (risk, execution mode, idempotency, dry run, evidence, scopes). The host reads it from descriptors and evaluates policy before the call. See Governance annotations. |
| The host owns the envelope | A plugin never sets who it acts as, which grant allows it, or its budget. The host builds the ActionContext after the policy decision. |
| References, not content | Payloads carry Reference and Digest values. Bytes are fetched through the host, which verifies the digest first. |
| Claims are checked, not trusted | A plugin result is a claim. The host derives outcomes, checks evidence ids and side effects, and measures cost and duration itself. |
| No credential in the plugin | A plugin receives a handle, usable only through the host's egress proxy, and only inside one invocation. See HostService. |
| Closed vocabularies | Enums are closed and zero is invalid; diagnostics, log entries and progress reports use a closed Reason enum with no free text. |
| Fail closed | Unknown enum numbers and unknown fields are rejected, so an older consumer refuses a newer value instead of misreading it. |
Package map
| Package | Contents | Reference page |
|---|---|---|
numinance.annotations.v1 | RiskClass, ExecMode, ActionOptions, FieldAnnotations and the action and field extensions | Governance annotations |
numinance.types.v1 | Shared enums, Digest, Reference | Shared types |
numinance.plugin.v1 | PluginService, HostService, Envelope, ActionContext, ActionResult, Diagnostic, Manifest | PluginService, HostService, Envelope |
numinance.ports.PORT.v1 | Nine typed ports: scm, ticketing, ciexecutor, authorexecutor, sandboxprovider, verifier, knowledge, evidencestore, transparencylog | Capability ports |
numinance.kinds.v1 | Sixteen configuration kinds and Metadata, Scope, MergeDirective | Kinds and JSON Schema |
numinance.evidence.v1 | record, attestation, checkpoint, anchor, bundle_manifest, inclusion_proof, reconciliation_report | Evidence |
numinance.events.v1 | Reference-only event payloads: change, envelope, evals, evidence, policy | Source files under proto/numinance/events/v1/ |
proto/numinance/
annotations/v1/ types/v1/ plugin/v1/ events/v1/ evidence/v1/ kinds/v1/
ports/{scm,ticketing,ciexecutor,authorexecutor,sandboxprovider,verifier,knowledge,evidencestore,transparencylog}/v1/How the packages fit
A plugin implements one or more ports. The host talks to it through PluginService (Handshake, GetManifest, GetDescriptors, Invoke, Health, Shutdown). During an Invoke, the plugin calls back through HostService to log, emit evidence, resolve references and request a credential handle. Everything the host decides about a call comes from the annotations in the port descriptors, the grant, and the policy decision, and ends as a record in the evidence chain. Configuration kinds are the customer-owned YAML that tells the runtime which ports, packs and floors apply.
Versioning and compatibility
Identifiers carry only the major version (numinance.ports.ciexecutor.v1, apiVersion: numinance.dev/v1); releases carry x.y.z. The policy is in VERSIONING.md in the spec repository.
| Artifact | MAJOR when | MINOR | PATCH |
|---|---|---|---|
Wire protocol numinance.plugin.vN | An RPC or field is removed or renamed; semantic change | A new RPC or field behind a protocol capability | Docs |
Port numinance.ports.PORT.vN | Any buf breaking FILE break; Core verb change; risk lowered | New capability, optional field, enum value | Docs |
Config kind numinance.dev/vN | A field is removed, renamed or retyped; a default changed | An optional field with since | Messages |
| Plugin | Drops a port or protocol major, a capability or a config field; widens permissions | New capability or action | Fix |
| Pack, policy or playbook | A new deny; a component removed | Additions; equal or looser decisions | Fix |
The since rule
Minors are additive and every addition carries since. For an RPC it is ActionOptions.since; for a field it is FieldAnnotations.since, required on every field added after 1.0.0. deprecated_in and replaced_by mark a field on its way out. Most elements are since: "1.0.0". Two examples of later additions are RequestCredentialResponse.egress_proxy (since: "1.1.0") and the entire ticketing port (since: "1.2.0"). A sender uses a field added in a later minor only when the negotiated spec version is at least that minor, or behind a negotiated capability, because the host rejects fields its descriptor copy does not know.
Major versions, deprecation and support windows
- Pre-GA stages.
v1alpha1is1.0.0-alpha.1(off by default) andv1beta1is1.0.0-beta.1(on by default).EvalSetis an alpha kind today. - Unknown fields. In YAML an unknown field is an error except under a reverse-DNS
extensions:map. On the wire, unknown fields are preserved by the protobuf layer but the plugin host decodesInvokemessages strictly and rejects them. - Engine support windows. The engine serves protocol majors N and N-1 for 24 months after N+1 reaches GA, and port majors M and M-1 for 12 months via shims. Official plugins are tested against engine N, N-1 and N-2.
- Deprecations ship with
deprecated_inandreplaced_by, aDEPRECATEDdiagnostic, a codemod and a conformance marker. - Lockfiles pin version, digest, installed-tree hashes and signer identity. Widening permissions always requires re-approval.
Before the first tag nothing is released: untagged SDK packages may change in breaking ways, and the pull request declares it. After the first tag the table above applies. The checkout these docs were verified against carries no tags, so treat the whole surface as pre-GA. Project-level rules are on Versioning.
buf configuration
The repository lints and breaks-checks the protocol with buf:
version: v2
modules:
- path: proto
lint:
use:
- STANDARD
- COMMENTS
except:
- FIELD_NOT_REQUIRED
- PACKAGE_NO_IMPORT_CYCLE
rpc_allow_same_request_response: false
rpc_allow_google_protobuf_empty_requests: false
rpc_allow_google_protobuf_empty_responses: false
service_suffix: ""
breaking:
use:
- FILE
ignore_unstable_packages: trueCOMMENTS means every element is documented in the proto itself; FILE-level breaking detection is what the "any buf breaking FILE break" rule in the table refers to. buf.gen.yaml generates Go (protoc-gen-go, gRPC and Connect stubs), Python and TypeScript code, plus JSON Schema 2020-12 for the configuration kinds only. See Generating clients for what is verified and what is pre-alpha.
Changing the protocol
Protected files (the annotations, for example) change only through an RFC and a security review. Four RFCs exist at the verified revision:
| RFC | Subject | Status at the verified revision |
|---|---|---|
| 0001 | host_only field annotation | Accepted |
| 0002 | resource_name field annotation | Accepted |
| 0003 | The evidence chain and export format, evidence/v1 | Open |
| 0004 | The EvalSet kind | Accepted |
See RFCs and Governance.
Failure modes and safeguards
| Situation | Result |
|---|---|
| A consumer sees an enum number it does not know | Rejects it, rather than guessing. |
| A plugin ships a modified copy of a port file | Install fails; the host copy is authoritative. |
| A change lowers the risk of an existing RPC | Counts as a major, contract-breaking change. |
| A newer field reaches an older host | Rejected by strict decoding; gate it on the negotiated spec version or a capability. |