Option types¶
The messages and enums declared in spec.proto: the types of message-valued options and of their fields.
StringConst¶
message
File-level string constants.
StringConst defines a named string constant that can be used in file-level options.
-
namestring=1Name is the identifier for the constant
-
valuestring=2Value is the string value of the constant
IntConst¶
message
File-level int constants.
IntConst defines a named integer constant that can be used in file-level options.
-
namestring=1Name is the identifier for the constant
-
valueint64=2Value is the integer value of the constant
FloatConst¶
message
File-level float constants.
FloatConst defines a named floating-point constant that can be used in file-level options.
-
namestring=1Name is the identifier for the constant
-
valuedouble=2Value is the floating-point value of the constant
BoolConst¶
message
File-level bool constants.
BoolConst defines a named boolean constant that can be used in file-level options.
-
namestring=1Name is the identifier for the constant
-
valuebool=2Value is the boolean value of the constant
DefaultHost¶
message
DefaultHost provides default values for host.Host fields.
Mirrors host.Host structure to avoid circular import with host.proto.
hoststring=1portint32=2
NumericConstraint¶
message
NumericConstraint defines constraints for integer fields (int32, int64, uint32, uint64)
This message supports complex validation scenarios beyond simple comparisons
-
gtint64=1gt: field must be greater than this value
-
geint64=2ge: field must be greater than or equal to this value
-
ltint64=3lt: field must be less than this value
-
leint64=4le: field must be less than or equal to this value
-
eqint64=5eq: field must equal this value
-
neint64=6ne: field must not equal this value
-
inrepeatedint64=7in: field must be one of these values
-
not_inrepeatedint64=8not_in: field must not be one of these values
-
multiple_ofint64=9multiple_of: field must be divisible by this value (field % multiple_of == 0)
FloatConstraint¶
message
FloatConstraint defines constraints for floating-point fields (float, double)
-
gtdouble=1gt: field must be greater than this value
-
gedouble=2ge: field must be greater than or equal to this value
-
ltdouble=3lt: field must be less than this value
-
ledouble=4le: field must be less than or equal to this value
-
epsilondouble=5epsilon: tolerance for floating-point equality comparisons (default: 1e-9)
Used when comparing floats to avoid precision issues -
inrepeateddouble=6in: field must be one of these values (within epsilon)
-
not_inrepeateddouble=7not_in: field must not be one of these values (within epsilon)
ParamMapConfig¶
message
ParamMapConfig configures automatic generation of FromMap methods
This generates a method that converts a map[string]string to the message type,
where the map keys correspond to enum values with json_return_value annotations
-
key_enumstring=1The enum type to use for parameter keys (e.g., "TokenParamKey")
The enum must have json_return_value annotations on its values -
strictbool=2Whether to return an error on unknown keys (default: false, ignore unknown)
-
validate_requiredbool=3Whether to return an error if required fields are missing (default: true)
OwnerDefault¶
message
OwnerDefault is the rights a type's owner holds over its own rows by default.
Both fields default to false, so an owner_default that names neither denies
both — the option is never a no-op, which is what makes a declaration
observable rather than decorative.
-
readbool=1read: the owner may read rows of this type.
-
writebool=2write: the owner may create or modify rows of this type.
SubscriptionAnnotation¶
message
SubscriptionAnnotation declares that this state service subscribes to
an event-bus topic from another service's state root.
-
sourcestring=1source is the fully-qualified topic reference: "package.StateRoot.field_name"
e.g., "telemetry.AgenticCodeEventBus.event_stream"
EventPublishAnnotation¶
message
EventPublishAnnotation declares that an actor publishes an EventBus topic.
topic_ref is canonical identity; auth policy is the standard tacplus method
auth shape carried through to the derived publish RPC.
topic_refstring=1method_s2sMethodS2SAnnotation=2method_c2sMethodC2SAnnotation=3
EventWatchAnnotation¶
message
EventWatchAnnotation declares that an actor watches an EventBus topic.
topic_ref is canonical identity; auth policy is the standard tacplus method
auth shape carried through to the derived watch RPC.
topic_refstring=1method_s2sMethodS2SAnnotation=2method_c2sMethodC2SAnnotation=3
RecordAnnotation¶
message
RecordAnnotation marks a message as a stored record: an event held in full by
the record_store service and projectable to timeseries stores. A record
IS an event (it flows through eventbus); every record declares a key field
(unique identifier) and an owner field (ownership/identity).
-
keystring=1key is the proto field name used as the unique record identifier.
-
ownerstring=2owner is the proto field name that identifies the record's owner (e.g. spiffe_id).
-
c2sMethodC2SAnnotation=3c2s, when present, exposes the record's publish RPC to external clients (via
gateway). A record IS an event; this declares its producer surface the same
way spec.event does. See EventAnnotation. -
s2sMethodS2SAnnotation=4s2s, when present, exposes the record's publish RPC to internal services.
It is the PRODUCER surface only; the consumer surface is s2s_subscribe. -
min_group_sizeint32=6min_group_size is the smallest result size this record's size-only query
surfaces will disclose. 0 / unset = no guard (additive, like owner_default)."Size-only" is the line: it covers the surfaces that return a size and no
rows (Get<T>Count, Subscribe<T>Count, and the QueryStats.size that
Update<T>Query returns for its newly-installed predicate). It deliberately
does NOT cover List<T>'s total_count, which accompanies rows the caller has
just been handed and so discloses nothing the rows themselves do not. A
future change that makes a paged read return counts without rows has
crossed that line. -
residencyResidencyPolicy=7residency is this record type's lifecycle, as two numbers.
idle_seconds no read and no write for this long -> the row leaves MEMORY and lives on disk only. The next touch reloads it; nothing is lost. deleted_max_age_seconds flagged deleted (see (spec.deleted_flag)) AND older than this -> the row is purged at the next sweep. Gone, not tiered.It is a data-lifecycle property of the type inside ONE store, not a choice
of backing store: field 50024 (cold_store) was retired above because
"tiering is internal to the store service", and that ruling stands. Tiering
is a CONSEQUENCE of these two clocks, never a declared class — which is why
the HOT/WARM/COLD preset enum is gone. There is no "never leaves memory";
long-lived, config-shaped data just carries a long idle_seconds.Both numbers are per type and both always have a value: 0/unset means "apply
the platform default" (see parser.DefaultResidencyIdleSeconds and
parser.DefaultResidencyDeletedMaxAgeSeconds), never "never". Unset is
therefore additive in the same way min_group_size and owner_default are.The idle clock lives ONLY in memory: a read never restamps
(spec.store_epoch), never writes the event log, never touches disk.
store_epoch keeps its own meaning — last WRITE, durable — and is what
deleted_max_age_seconds measures the delete from.Example:
-
mutabilityMutability=8mutability is whether this record type's rows may be changed after they are
written. It is an ATTRIBUTE, orthogonal to residency: an immutable type
(accounting) and a mutable one are both legitimate under any pair of
residency clocks.Unset = MUTABILITY_CRUD, which is what every record does today.
MUTABILITY_APPEND_ONLY is enforced at BOTH ends or it is decoration: the
generator emits no row-mutating RPC for the type (ValidateMutabilityAnnotations
refuses one at generation), and the generated service wrapper refuses to host
one. This is the same two-ended shape (spec.record).min_group_size already has. -
s2s_subscribeMethodS2SAnnotation=9s2s_subscribe, when present, exposes the record's SUBSCRIBE RPC to the named
internal services. It is the consumer half of the pair whose producer half
is s2s (field 4), and it exists because a topic has two boundaries and only
one of them was declarable.A record's synthesized <Record>Bus topic wrapper hosts both PublishTo<T> and
SubscribeTo<T>. s2s names who may PUBLISH. Nothing named who may CONSUME, so
the subscribe RPC could only ever fall back to the state service's
service-level list, which for a synthesized wrapper is resolved from the
owning PACKAGE — and the record store that consumes every record topic is
never in the owning package. Under per-topic authorization that is
fail-closed: the store is denied on every topic it exists to read.Declared rather than inferred on purpose. Treating "the record store" as an
implicitly authorized consumer would put an undeclared trust relationship in
the generator with no proto anyone could read to audit it, and would silently
grant or silently withhold access to any FUTURE consumer of a record topic.
Who may read a record's stream is exactly as much a policy statement as who
may write it, so it is written down in the same place, in the same shape.Same semantics as s2s: ["*"] is any authenticated service, an explicit list
is exactly that list, and an absent annotation leaves the service-level
default in place (i.e. this is additive — a record that declares none keeps
today's behaviour).Example — a stored record consumed by the record store:
-
s2s_readMethodS2SAnnotation=10s2s_read, when present, exposes this record's STORED-ROW READ surface to the
named internal services — the record query plane's S2S half, hosted by the
record store.It is the third member of a set whose other two already exist, and it is
declared for exactly the reason s2s_subscribe is. A record type has three
boundaries, not two:s2s who may PUBLISH onto the record's topic (write the stream) s2s_subscribe who may CONSUME the record's topic (tail the stream) s2s_read who may READ the record's STORED ROWS (query the store)Until this field existed the third boundary had no per-record declaration at
all: every record's read RPCs carried the TRAIT-WIDE (spec.trait).auth_services
list, so authorizing one service to read one record type authorized it to read
EVERY record type in the fleet. That is precisely what
decisions/mcp-generation-and-service-owned-data-2026-08-31.md §2 rules out —
"The record store grants entitlements per service, per record type, INDEPENDENTLY" —
and it is why that document's §1 calls the sharedquerytrait superseded.
The trait-wide list stays as the fallback for records that declare nothing,
so this is additive exactly the way s2s_subscribe, min_group_size and
owner_default are.Declared rather than inferred, on the same reasoning s2s_subscribe records:
treating "the service that produces a record type" as an implicitly
authorized reader would put an undeclared trust relationship in the
generator with no proto anyone could read to audit it. A producer is not
automatically a reader — §2's whole point is that read and write rights are
granted independently — so the read right is written down where the other two
are, in the same shape.PAIRED CONTROL. This is the grant half. The USE half is
(spec.record_read) on the reading service's own proto, and the generator
refuses a build where one is declared without the other: a service that
declares (spec.record_read) on a record whose s2s_read does not name it
fails generation rather than emitting a client that would be denied at
runtime. Neither end can drift from the other, because neither end works
alone.Same semantics as s2s and s2s_subscribe: ["*"] is any authenticated service,
an explicit list is exactly that list, and an absent annotation leaves the
trait-wide default in place.Declaring it also makes the record's KEY field an EQ criterion on the
generated <T>Filter, whether or not the key carries its own
(spec.query_field). Point-read-by-key is the read surface's most basic
question and the plane could not express it otherwise; see
record_map.go's keyFilterCriterion.Example — a record its own producer reads back:
option (spec.record) = { key: "delivery_id" owner: "spiffe_id" s2s: {auth_services: ["messaging"]} s2s_subscribe: {auth_services: ["recordstore"]} s2s_read: {auth_services: ["messaging"]} };CROSS-TENANT DOCTRINE. auth_services is an identity grant, not a namespace
scope: an authorized peer service reads FLEET-WIDE, across every tenant's
rows, same as today. The C2S half of this same plane (spec.c2s_read below)
pins the caller to their own token namespace; the S2S half deliberately does
not, because narrowing a peer's reads to its own service namespace would not
be a tenant boundary — it would break every cross-tenant peer aggregation
this plane exists to serve (see the identical reasoning already written
where it is enforced in code:
pkg/internal/java/types/recordnamespace_gen.go's "C2S ONLY" comment).
Which rows a peer may see is the auth_services decision, made before the
handler runs — there is no second, namespace-shaped decision underneath it.This changes for ONE case: a tenant-owned service's own S2S RecordMap reads
(a caller whose token namespace is /tenants/<t>, calling a service declared
(spec.service).tenant_owned) gain the same pin+filters the C2S half already
has — /system/* callers are unaffected and stay fleet-wide. That is separate,
not-yet-generated runtime behavior (tenant-owned-service-identity goal item
\3); until it lands, every S2S reader — tenant-owned service or not — reads
fleet-wide exactly as described above. -
c2s_readMethodS2SAnnotation=11c2s_read, when present, names the services allowed to RE-EXPOSE this
record's stored rows TO THEIR OWN END USERS — the client-facing read plane,
hosted by the reading service itself and never by the record store.It is the FOURTH boundary, and it is not a narrowing of the third:
s2s who may PUBLISH onto the record's topic (write the stream) s2s_subscribe who may CONSUME the record's topic (tail the stream) s2s_read who may READ the record's STORED ROWS (query the store) c2s_read who may DISCLOSE those rows TO A PERSON (re-expose them)A service can hold s2s_read without c2s_read and that combination is the
common one: read a record type to make an internal decision, and never show
it to anyone. Making disclosure a separate grant is what keeps that
distinction sayable, and it keeps the RECORD OWNER in the loop on every
re-exposure of its data rather than only on the first read of it.TWO SEMANTIC DIFFERENCES FROM s2s_read, both deliberate:
["*"] FAILS GENERATION. On s2s_read the wildcard means "any authenticated peer inside the fleet", which is broad but is a statement someone might mean. "Any service may show my rows to any of its users" is not a statement anyone means to make, so it is refused rather than supported. c2s_read IMPLIES s2s_read for the same service, and the pair with the second half missing fails generation. The generated c2s handler's entire data path IS the s2s reader; a disclosure grant without a read grant is a surface that cannot execute.PAIRED CONTROL, exactly as s2s_read is. This is the grant half; the use half
is (spec.record_read).c2s on the reading service's own proto, and
ValidateRecordReadAnnotations refuses a build in which one is declared
without the other, in both directions.Example — a record its producer also shows to its users:
-
privatebool=12private marks this record type as belonging to exactly one principal —
"a user's data and nobody else's, ever." Unset = MUTABILITY_CRUD's kind of
default: today's behaviour, where a record's boundary is its tenant
(namespace) and every service auth_services names may see every row in it.The FIELD NAME here is deliberately NOT reused for the parsed annotation
key an author-facing check would grep for: it is carried internally as
"record_private" (see descriptor.go), never bare "private" — that string
already names the UNRELATED per-FIELD statedb marker
(spec.private = 50013, "field can never be accessed by other services").
The two guard completely different things at completely different scopes
(a whole record type here, one state field there) and must never be
confused by a validator or a human skimming annotation keys.What true means, once the generated pins land (tracked, not yet emitted by
this field alone):- The row's owner is stamped from the caller's `sub` at write time, never caller-supplied — (spec.store_principal) already does the stamp. - Every generated read path (Get, List, Watch, Count, Sample, the record-plane readers, the S2S readers) pins owner == caller BEFORE any other criterion — a pin the query cannot see, not a filter it can widen. - No admin or tenant override: a tenant admin's token reads nothing from a private record, by generation, not by policy. - A service reading on a user's behalf must carry that user's delegated token (the `act` claim); its own service identity alone gets PERMISSION_DENIED.What this field alone already means, checked at generation time
(ValidateRecordPrivateAnnotations):- the record MUST also declare (spec.store_principal) on the field holding its owner — that is what the generated pin will bind to, and a private record naming no such field has nothing to pin, the same "record nobody remembered to annotate" failure (spec.store_namespace) already refuses for the weaker tenant boundary. - the record refuses s2s_subscribe. A subscriber to a record's raw topic is a SERVICE, not a per-request caller — there is no principal in scope for a stream handler to pin against, so declaring both is a fleet-wide broadcast of every row ever written, the exact thing `private` exists to prevent. - the record refuses s2s_read, for the same reason one surface over. The record query plane's S2S half is peer-facing: the caller is a SERVICE identity, so there is no principal for the owner pin to bind to and the only gate the generated wrapper can apply is the (spec.store_namespace) tenant backstop. The plane therefore emits NO peer-facing read RPC for a private type (record_map.go), and the grant is refused here rather than accepted and authorizing nothing. c2s_read falls with it — the c2s reader's data path IS the s2s reader — and so does the matching (spec.record_read) on any would-be reader.s2s stays declarable: that is the WRITE topic, not a read. The read surface
a private record keeps is the plane's C2S half, which carries a user token
— the one identity the owner pin can bind to. That pin is the remaining,
not-yet-emitted half of this annotation (decisions/
identity-people-tenant-devices-private-2026-09-21.md §4).Example — a record only its own owner may ever read:
ResidencyPolicy¶
message
ResidencyPolicy states a record type's lifecycle as two clocks. It replaced
the HOT/WARM/COLD preset enum on 2026-09-20: tiering is a consequence of the
clocks, not a declared class, and there is no "never leaves memory".
Both fields: 0/unset means APPLY THE PLATFORM DEFAULT, never "never". The
defaults are parser.DefaultResidencyIdleSeconds and
parser.DefaultResidencyDeletedMaxAgeSeconds.
Field numbers 1 and 2 are REUSED, not reserved: they held the retired
window / window_seconds preset pair. This message is a compile-time
annotation — no wire data of it is ever persisted — and an unknown field
name is a hard protoc error where a reserved one is silently discarded, so
reuse is what makes a proto still saying residency: {window: ...} fail
loudly rather than generate with its intent dropped.
-
idle_secondsint64=1idle_seconds is how long a row may go with NO read and NO write before it is
evicted from memory. It lives on disk beneath, complete and queryable, and
the next touch reloads it — eviction is cache maintenance, never data loss.A read that counts is a Get by key and every row actually RETURNED by List
or Watch. A Count, a Subscribe…Count, and a row that merely matches a
predicate do not, or a dashboard counting a dataset would keep it resident. -
deleted_max_age_secondsint64=2deleted_max_age_seconds is how long a row flagged deleted (see
(spec.deleted_flag)) survives after the delete write. Measured from that
write's (spec.store_epoch). Past it the row is DEAD: never loaded into
memory again, removed by the next sweep, not tiered and not readable.A record type that declares no (spec.deleted_flag) field has nothing for
this clock to measure; it is carried anyway rather than refused, because a
type that later gains the marker should not also have to gain a number.
EventAnnotation¶
message
EventAnnotation marks a message as an EventBus event: tacplus generates a topic
and a publish RPC for it. The producer surface is declared (not defaulted):
set c2s to expose the publish RPC to external clients (via gateway), s2s to
expose it to internal services. At least one must be present; an internal-only
event sets only s2s and is never exposed externally. spec.record implies
spec.event (a record is an event that is also stored + projectable).
-
c2sMethodC2SAnnotation=1c2s, when present, exposes the publish RPC to external clients (via gateway).
-
s2sMethodS2SAnnotation=2s2s, when present, exposes the publish RPC to internal services.
Producer surface only; the consumer surface is s2s_subscribe. -
s2s_subscribeMethodS2SAnnotation=3s2s_subscribe, when present, exposes the SUBSCRIBE RPC of this event's topic
to the named internal services. See RecordAnnotation.s2s_subscribe for the
full reasoning; the semantics are identical.
DedupPolicy¶
message
DedupPolicy is a store-neutral statement of row identity and duplicate
resolution. A downstream materializer maps this onto an engine's merge/dedup
mechanism; tacplus carries only the semantic intent.
strategyDedupStrategy=1-
key_fieldsrepeatedstring=2logical row identity
-
version_fieldstring=3selects winning row under DEDUP_LATEST_BY_VERSION
Transformation¶
message
Transformation declares a pure typed conversion of this message into a
derived row type: source message A -> derived type B. tacplus generates the
derived message with its column annotations and projection_source mappings.
A transformation says NOTHING about delivery. It does not require the source
to be a (spec.record), it does not create or subscribe to an EventBus topic,
and it carries no retention window — retention is delivery-side state and
lives on (spec.record).retention. Declaring a transformation and declaring
delivery are independent: either generates without the other.
The shape is store-neutral: dedup and cardinality_hint are declared as
semantic intent, and a downstream materializer renders the concrete
engine/merge behavior.
-
namestring=1name is the derived row type name (e.g. "SessionRecord").
-
whenstring=4when is the oneof variant name that triggers this row (e.g. "session").
Only source messages carrying this payload variant produce a row. -
fieldrepeatedTransformationField=5field lists the source fields projected into the derived type.
-
dedupDedupPolicy=7dedup is the store-neutral duplicate-resolution policy for the derived rows.
TransformationField¶
message
TransformationField maps a source message field to a derived-type column.
-
sourcestring=1source is the field path on the source message (e.g. "session.model").
-
asstring=2as renames the column in the derived type (defaults to last segment of source).
-
order_keyint32=3order_key is the column's position in the store-neutral sort-key role
(0 = not a sort key). A materializer maps this to the engine's sort order. -
partition_keybool=4partition_key marks this column as part of the store-neutral partition role.
-
cardinality_hintCardinalityHint=6cardinality_hint is a store-neutral hint about the column's distinct-value count.
TimeseriesTypeAnnotation¶
message
TimeseriesTypeAnnotation opts a message into timeseries table generation.
tacplus generates store-neutral conversion helpers and a gRPC service from
messages annotated with this option. Each annotated message produces one
timeseries table type. Retention and dedup are declared as semantic intent;
a downstream materializer renders the concrete engine/TTL/merge behavior.
Example:
This is a GENERATOR-INTERNAL compile target: tacplus emits it from every
(spec.transformation). It carries no retention — retention is delivery-side
state and lives on (spec.record).retention.
-
namestring=1Table name. Default: snake_case of message name.
-
sourcestring=4Source event FQN for field extraction.
When set, every field in this message must have (spec.projection_source) to map
from the source event to this projection.Example:
"sensor.SensorEvent" -
dedupDedupPolicy=6dedup is the store-neutral duplicate-resolution policy.
TimeseriesFieldOption¶
message
TimeseriesFieldOption configures how a single proto field maps to a timeseries
column. Applied via (spec.timeseries_column) on individual fields. Any field
with this annotation becomes a column; fields without it are omitted. Column
roles are store-neutral: order_key/partition_key name logical roles and
cardinality_hint states distinct-value intent — a downstream materializer maps
them onto the concrete engine's sort/partition/encoding mechanisms.
Example:
string session_id = 1 [(spec.timeseries_column) = {order_key: 1}];
int64 timestamp_ms = 2 [(spec.timeseries_column) = {order_key: 2, partition_key: true}];
string event_type = 3 [(spec.timeseries_column) = {cardinality_hint: CARDINALITY_LOW}];
int64 duration_ms = 4 [(spec.timeseries_column) = {}];
-
order_keyint32=3Position in the store-neutral sort-key role (1-indexed). 0 = not a sort key.
-
partition_keybool=4Part of the store-neutral partition role.
-
cardinality_hintCardinalityHint=7Store-neutral hint about the column's distinct-value count.
ServiceS2SAnnotation¶
message
ServiceS2SAnnotation declares a service-to-service gRPC service.
Only authenticated service identities can call these RPCs.
Per-RPC auth lives on spec.rpc: allowed callers (auth_services) and the
bootstrap corner cases (RPC_FLAG_SELF_AUTHENTICATES) are declared there.
-
auth_servicesrepeatedstring=1auth_services: service identities allowed to call (service-level default).
["*"] = any service. Named entries restrict to those identities.
Overridden per-RPC by spec.rpc auth_services; tacplus-generated service
protos (state, record_map, timeseries, opa_eval) still declare it here. -
labelstring=3label is the routing label for the physical service that hosts this S2S
interface. Used for vservice routing tables where the interface is defined
in a vservice proto but hosted by a physical service (e.g. "recordstore").
ServiceC2SAnnotation¶
message
ServiceC2SAnnotation declares a client-to-service gRPC service.
Exposed through the gateway to end users.
Per-RPC auth lives on spec.rpc: an RPC declares its own policy through
RPC_FLAG_PUBLIC_NO_AUTH / RPC_FLAG_AUTH_ANY_USER / RPC_FLAG_AUTH_ENTITLED_USERS.
Default policy: entitled users (gateway enforces entitlements).
-
auth_entitled_usersbool=3auth_entitled_users: users with matching entitlements can call.
This is the default C2S policy; set explicitly when entitlement
checks are required for all RPCs in this service. -
labelstring=4label is the routing label for the physical service that hosts this C2S
interface, the twin of (spec.service_s2s).label. A trait declares its
client-facing surface in its own proto and names the implementor here, so
the surface is not locked to the implementor's identity.The label names the HOST (whom the gateway dials). The AUDIENCE the
gateway checks the caller's token against is the file's (spec.trait).name,
not this label: the audience is the contract, so a second implementor of
the same trait needs no client change. A label no loaded service serves
fails generation, and so does a label on a file that declares no trait.
ServiceC2DAnnotation¶
message
ServiceC2DAnnotation declares a client-to-daemon gRPC service.
Served over local IPC between CLI processes and a daemon.
No auth required — filesystem permissions are the security boundary.
-
socket_pathstring=1All c2d services are unauthenticated and localhost-only.
socket_path is the Unix domain socket the daemon binds and clients dial.
Required — there is no default. A shared default path would collide between
daemons on the same host, so every c2d service declares its own path.
IdentitySpiffeResource¶
message
IdentitySpiffeResource declares that a string field holds a resource SPIFFE ID
that tacplus must be able to CONSTRUCT from sibling fields (SPIFFE plan D5),
via resource_spiffe_id(trust_domain, <namespace_field>, kind, <id_field>).
Generation FAILS when the referenced sibling fields are absent, so a resource
owner identity can never be hand-formatted or left underspecified.
string owner_spiffe = 2 [(spec.identity_spiffe_resource) = {
namespace_field: "tenant_namespace";
kind: "gitlab-event";
id_field: "event_id";
}];
-
namespace_fieldstring=1namespace_field is the sibling field holding the namespace path.
-
kindstring=2kind is the resource kind segment (a constant, validated as a single segment).
-
id_fieldstring=3id_field is the sibling field holding the resource id.
TokenMinterAnnotation¶
message
TokenMinterAnnotation marks a service as the cluster's token minter, declared
EXPLICITLY rather than inferred from RPC signatures. The bootstrap token
exchanger (service_im / client_im codegen) targets the service carrying this
annotation, resolving its fully-qualified package.Service. Exactly one
authoritative minter should be annotated.
-
proxybool=1proxy marks the client-reachable C2S front (e.g. gateway.Mint) that proxies
to the authoritative S2S minter. Clients with no S2S identity mint through
this proxy; services with an S2S identity call the authoritative minter
(proxy = false) directly.
SmbAnnotation¶
message
SmbAnnotation marks a Service Mesh Blackbox service and selects its trust root.
The default root is SMB_ROOT_ISOLATED.
rootSmbRoot=1
SmbAuthorityAnnotation¶
message
SmbAuthorityAnnotation marks the consumer service that issues per-SMB CAs and
member certs for spec.smb services (SPIFFE plan D7), declared
EXPLICITLY rather than inferred. tacplus resolves the fully-qualified
package.Service carrying this annotation as the SMB cert authority and fails
generation if any spec.smb service exists with no authority declared. tacplus
generates the authority contract and the SMB service's client seam; the
designated consumer service implements the server side (CA keypair custody,
member-cert signing, rotation). Exactly one authority should be annotated.
No fields.
VolumeGroupAnnotation¶
message
VolumeGroupAnnotation declares one named volume group in the platform blob
store, owned by the service carrying the annotation. A volume group is a set
of build-time rules (accepted content types, per-blob size ceiling, per-tenant
quota) that the blob store enforces at upload; groups are never created at
runtime, so a service that stores blobs MUST declare its group here. The
annotation is repeatable: one service may declare several groups. tacplus
refuses duplicate group names across the whole fleet and surfaces every
declared group in registry.json so the operator can render the blob store's
config; tacplus does not implement the blob store.
-
namestring=1name is the volume group's identifier. Must be unique across the whole fleet.
-
max_blob_bytesuint64=2max_blob_bytes is the largest single blob this group accepts.
-
content_typesrepeatedstring=3content_types is the allow-list of MIME types this group accepts, e.g.
"application/pdf". Each entry must be a well-formed media type (validated at
build time; the runtime service still checks magic bytes per-upload). -
tenant_quota_bytesuint64=4tenant_quota_bytes is the per-tenant storage quota within this group.
MethodS2SAnnotation¶
message
MethodS2SAnnotation overrides service-level defaults for a single S2S RPC.
Only valid on methods within a service_s2s service.
-
auth_servicesrepeatedstring=1auth_services overrides the service-level default for this method.
-
auth_disabledbool=2auth_disabled: this RPC handles its own authentication internally.
The caller interceptor skips this RPC entirely. Used for bootstrap
bootstrap token-minting RPCs where callers authenticate via
credentials in the request body (cred pair JWT), not via bearer token. -
routing_only_servicesrepeatedstring=3routing_only_services narrows the TYPE a named subscriber's generated
handler receives. Only meaningful on s2s_subscribe (the consumer half of a
record/event surface); ignored everywhere else.Listing a service here GRANTS it nothing. Authorization stays auth_services'
job alone; this field only narrows what an already-authorized subscriber's
generated code is typed to see. auth_services keeps its existing meaning
unchanged, and a subscriber not listed here keeps receiving the full
message exactly as before (this is additive).For a listed subscriber, tacplus synthesizes
message <T>RoutingEnvelope { string delivery_id = 1; // <T>'s (spec.record).key field string recipient = 2; // <T>'s (spec.store_principal) field string namespace = 3; // <T>'s (spec.store_namespace) field }and that subscriber's generated Handler takes <T>RoutingEnvelope instead of
<T>. Generation FAILS for a type flagged here that declares no
(spec.store_principal) field (and likewise no (spec.record).key or
(spec.store_namespace) field, or one of the three that is not a string):
an envelope with an empty recipient is a routing table nobody can route
with, so it is refused rather than emitted.WHAT THIS IS NOT. This is a TYPED boundary, not a memory guarantee. The
FULL message body still arrives on the wire and is still fully decoded
inside the subscriber's process; the generated dispatch projects the three
fields out and drops the decoded message immediately afterwards. The body
is therefore still reachable from a heap dump, a debugger, or anything that
logs the decode buffer. What this field delivers is that the subscriber's
own handler CANNOT be written against the body, because its argument type
does not have one. Narrowing the wire itself is a separate, broker-side
change and is not what this field does.
MethodC2SAnnotation¶
message
MethodC2SAnnotation overrides service-level defaults for a single C2S RPC.
Only valid on methods within a service_c2s service.
-
auth_disabledbool=1auth_disabled: no token required for this RPC.
Used for public endpoints like OIDC discovery and JWKS. -
auth_any_userbool=2auth_any_user: any authenticated user can call this RPC
(no entitlement check). Used for user-facing endpoints where
any valid token is sufficient (e.g., token exchange, event streams). -
target_namespace_fieldstring=5target_namespace_field names the request field whose string value carries
the target namespace path (e.g. "/io/example/tenant"). When set, the C2S
authorizer enforces the level-or-below rule: the caller's namespace (from
the bearer token'snamespaceclaim) must equal the target namespace or
be a strict path-segment ancestor of it. The field must exist on the
request message and be of type string.Use the proto field name (snake_case as declared), e.g. "tenant_namespace".
Leave unset for RPCs whose target is implicit (e.g. List* methods that
scope the response to the caller's subtree at the handler).
RpcAnnotation¶
message
RpcAnnotation declares an RPC's call surface on two independent axes plus a
set of non-exclusive protocol flags. It is per-RPC: the axes are properties
of the call, not of the service that happens to host it.
Example: option (spec.rpc) = {principal: RPC_PRINCIPAL_SERVICE};
Example: option (spec.rpc) = {
principal: RPC_PRINCIPAL_USER,
exposure: RPC_EXPOSURE_EXTERNAL,
flags: [RPC_FLAG_AUTH_ENTITLED_USERS]
};
-
principalRpcPrincipal=1principal is required; RPC_PRINCIPAL_UNSPECIFIED is not a valid declaration.
-
exposureRpcExposure=2exposure defaults to RPC_EXPOSURE_INTERNAL.
-
flagsrepeatedRpcFlag=3flags is an unordered set; RPC_FLAG_UNSPECIFIED is not a valid entry.
-
auth_servicesrepeatedstring=4auth_services lists the service identities allowed to call this RPC.
["*"] = any authenticated service. Per-RPC: two RPCs of one service may
legitimately admit different callers.On an RPC_PRINCIPAL_USER rpc of a service_c2s service it ADDS the listed
service identities to the users the rpc already admits: a user token is
still authorized by its entitlements, a service token by these patterns,
and a service not listed is refused. This is how one rpc is "c2s for
users + s2s for these services"; the handler's Caller is whichever
identity the verified token names. -
target_namespace_fieldstring=5target_namespace_field names the request field whose string value carries
the target namespace path (e.g. "/io/example/tenant"). When set, the
authorizer enforces the level-or-below rule: the caller's namespace (from
the bearer token'snamespaceclaim) must equal the target namespace or
be a strict path-segment ancestor of it. The field must exist on this
RPC's request message and be of type string.Use the proto field name (snake_case as declared), e.g. "tenant_namespace".
Leave unset for RPCs whose target is implicit (e.g. List* methods that
scope the response to the caller's subtree at the handler). Under
RPC_FLAG_AUTH_ANY_USER this instead holds an absolute namespace literal. -
mcp_tool_namestring=6mcp_tool_name overrides the generated MCP tool name for a
RPC_PRINCIPAL_MCP rpc. Leave it unset: the default is the RPC name in MCP
naming convention (snake_case), and the tool description is the rpc's own
leading // doc comment, so neither is a second surface that can drift from
the real one. Set it only for the rare RPC whose name does not make a good
tool name. Must be snake_case ([a-z][a-z0-9_]*), and is only meaningful on
RPC_PRINCIPAL_MCP — declaring it elsewhere fails generation rather than
being silently ignored. -
auth_actorsrepeatedstring=7auth_actors lists the delegate identities allowed to call this RPC on
another principal's behalf. Entries are matched against the token'sact
claim — the delegate's service path, the same shape auth_services patterns
already match — as anchored RE2; ["*"] = any actor.The gate runs before the auth_services / entitlement checks, and only when
the token actually carriesact: a token with noactclaim is authorized
exactly as it is today. A token that DOES carryactis rejected unless
this RPC declares auth_actors and one of its patterns matches, so an RPC
that says nothing about delegation admits no delegated call at all. -
requires_devicebool=8requires_device declares the DEVICE-ONLY APPROVAL check: this RPC is served
only when the caller presents a pending approval the person consumed with a
WebAuthn assertion from a device linked to them. A browser IdP session, a
delegated agent token and a tenant admin all fail it, however completely
they are the user.The generated server check reads
x-tacplus-approval-idand
x-tacplus-device-assertion-binoff the call metadata — never request
fields, so neither is inside the hashed bytes — computes the canonical hash
of the request, and calls the authoritative (spec.token_minter) service's
BrokerPendingApproval. Any failure refuses the RPC: the handler is never
reached. The S2S client for that call is auto-injected into the declaring
package; the author writes only this boolean.The proposing half (creating the pending approval) is the caller's own
call, not generated. The caller-side generated helper computes the request
hash with the SAME canonicalization the check uses and attaches the two
metadata keys. -
requires_device_whenRequiresDeviceWhen=9requires_device_when narrows the same DEVICE-ONLY APPROVAL check above to
the requests where ONE named request field holds ONE named value. It exists
for the unified-CRUD shape: an rpc that writes three kinds of row, only one
of which is a decision about another person, where marking the whole rpc
would gate the two that are not.It is mutually exclusive with requires_device: a conditional gate is a
narrower requires_device, not an addition to it. Everything else is
identical — when the condition holds, the SAME check runs over the SAME
canonical hash of the WHOLE request, so every structural precondition
(verified caller, plain unary rpc, no map field in the request tree) still
applies to the whole rpc, unconditionally.
RequiresDeviceWhen¶
message
RequiresDeviceWhen is the condition (spec.rpc).requires_device_when gates on.
Both halves are resolved at generation time against the rpc's request
descriptor: an unknown field, a field that is not a plain string or enum, or
an enum value name the enum does not have all fail generation rather than
becoming a comparison that is silently never true.
-
fieldstring=1field is a top-level field name on the RPC's request message. It must be a
plain (non-repeated, non-map, no explicit presence) string or enum field. -
equalsstring=2equals is the value that field must hold for the gate to apply — the enum
value's short name (e.g. "RULE_EFFECT_DELEGATION") for an enum field, or
the literal string for a string field.
MethodCollection¶
message
MethodCollection marks an RPC as a collection-returning read and declares its
shape. The author declares intent plus a bound; tacplus derives transport,
tiering, flow control, and lifecycle. See docs/API.md for the full model.
-
modeCollectionMode=1mode is required: SNAPSHOT (finite) or WATCH (open-ended).
-
order_byrepeatedstring=2order_by names the response fields that define a stable total order.
Required for both modes (pagination cursors and the snapshot/delta boundary
are only coherent over a stable order). tacplus appends a unique tiebreaker
if these are not already total. -
max_itemsuint32=3max_items is the required, positive, finite item cap for SNAPSHOT reads.
It is the routing key: tacplus computes max_items * max_item_bytes to pick
the delivery tier. Forbidden on WATCH. Zero/unset on SNAPSHOT is an error. -
replay_depthuint32=4replay_depth is the WATCH replay-ring depth (count of retained changes).
Optional; defaults to a platform constant. Forbidden on SNAPSHOT. -
default_page_sizeuint32=5default_page_size is the PAGE per-page window used when the request omits
page_size. Required positive on PAGE; must be <= the trust-tier ceiling.
Forbidden on SNAPSHOT/WATCH. PAGE only. -
revision_fieldstring=6revision_field names the int64 field on the WATCH delta message that
carries each change's revision — what the replay ring is keyed by and what
a caller's from_revision resumes against. Optional; defaults to "revision".
WATCH only.It exists because the delta is not always a purpose-built envelope. Where
it is (<T>WatchChange { item; revision }) the default is right and nothing
is declared. Where the delta IS the row — an append-only record has no
update or delete frame to distinguish, so the change is the new row — the
row names its own sequence, and that name is the row's to choose:
scottypersonal.ChatEvent carriesseq, per-owner monotonic, and is
already the keyset ListChatEvents pages over. Forcing a second int64
calledrevisiononto such a row would mean two ordering fields that must
agree forever.The named field must exist on the delta message and be a singular int64.
Generation fails otherwise — including for the default — rather than
emitting a wrapper that only fails later in cargo/javac/go build.
ServiceEventAnnotation¶
message
ServiceEventAnnotation declares that a state service's topic/queue RPCs
are accessible to other services (S2S only, like service_state).
-
labelstring=1label is the routing label for service resolution (e.g. "eventbus").
-
exportbool=3export makes this event topic available for RO import by other services.
ConditionalRequirement¶
message
ConditionalRequirement defines a conditional requirement for a field.
The field is required when a sibling field equals a specific value.
-
fieldstring=1field is the sibling field name (snake_case) to check
-
valuestring=2value is the enum value name or string to compare against
QueryFieldOption¶
message
QueryFieldOption configures a record field as a RecordMap query filter criterion.
opQueryOp=1
RedactOption¶
message
RedactOption configures a field (or, at message level via (spec.data_entitlement),
a whole message default) with a data-entitlement mode.
modeRedactMode=1
StateAnnotation¶
message
StateAnnotation marks a message as a state root and references its backing store.
-
namestring=1name is the state root name (e.g. "ClusterState", "AegisState").
-
labelrepeatedstring=3label lists the routing labels for the backing stores this state root
routes to (e.g. ["state-store"], ["kernel", "edge"]). Every label must be
served by a loaded service; a label no service serves fails generation. -
auto_tlsTlsKeyType=4auto_tls enables automatic TLS certificate lifecycle management.
When set, tacplus generates a bidi stream subscription to the certificate
authority service and a new_tls_cert Handler method for cert delivery.
The service generates keypairs locally; the certificate authority signs and
delivers certs, triggering renewal at half-life. -
exportbool=5export makes this state root available for RO import by other services.
Without export, the state is private to the owning service. -
requires_artifact_on_writebool=6requires_artifact_on_write marks this state root as "the living side" —
user-owned/user-facing data whose mutation must leave a durable artifact.
When set, every WRITE-classified RPC (spec.internal/common/verbs registry)
on a non-synthesized service declared in the same package must be
accompanied by at least one (spec.record)-annotated message in that
package; a write RPC with no record in scope fails generation
(ValidateArtifactOnWrite). Control-plane/config state roots (kernel, opa,
sentinel, oauth, letsencrypt, operator) must NOT set this — it is only for
state that represents user-owned knowledge, not service configuration. -
identity_snapshotbool=7identity_snapshot marks this state root as kernel's OWN identity snapshot
(decision 2026-09-25: "[Kernel] keeps exactly one durable thing: its OWN
identity snapshot — signing keys, CAs, tenant tree, grants, links,
principals") rather than app data parked in a kernel-labelled boundary.
A field on a state root with this set may carry (spec.persistence) even
though (spec.service_state).label includes "kernel" — the one exception
to Rule 3b. Only meaningful on a state root whose label includes
"kernel"; setting it on any other root does nothing.
ServiceStoreAnnotation¶
message
ServiceStoreAnnotation marks a message as a durable key-value store.
Unlike service_state (convergent, fan-out, Watch), service_store is:
- Write-with-quorum (writes acked only after replication to 2+ nodes)
- Read-on-demand (no Watch streams, no client-side caching)
- No boundary decomposition (the message IS the value)
The store_key field annotation marks which field is the entry key.
Access is via delegate pattern: services authenticate clients, then
write through the backing service. External clients never write directly.
-
labelrepeatedstring=1label lists the routing labels for the backing stores this store routes to
(e.g. ["state-store"]). Every label must be served by a loaded service; a
label no service serves fails generation.
TimeseriesStoreAnnotation¶
message
TimeseriesStoreAnnotation marks a service proto as a timeseries data store.
The store receives its projection data by auto-subscribing to the EventBus
topics that carry its projection sources; no mutation service is generated.
No fields.
RecordStoreAnnotation¶
message
RecordStoreAnnotation marks a service proto as the full-record store.
Services with this annotation auto-subscribe to EventBus topics that publish
the record types they store (currently spec.record). Hot/cold tiering is
internal to the service.
No fields.
ServiceHealthAnnotation¶
message
ServiceHealthAnnotation declares a typed health message for a service.
The annotated message MUST embed healthreport.ServiceInstanceHealth as field 1
("base"). The state store extracts .base for the generic health view and stores the
full typed message per service (keyed by instance_id).
Each node reports its own perspective of cluster health — Sentinel monitors
consistency across perspectives and raises alarms on divergence.
No fields.
VServiceAnnotation¶
message
VServiceAnnotation declares a virtual service identity for a proto file.
Virtual services have their RPCs hosted by a physical service (e.g., eventbus)
but are authorized separately. The id is a stable field number for TokenEntitlements
(use range 30+ to avoid collision with physical service IDs).
-
namestring=1name is the canonical vservice name used as the entitlement key (e.g., "telemetry").
-
idint32=2id is the stable field number in TokenEntitlements for this vservice.
TraitAnnotation¶
message
TraitAnnotation declares a trait identity for a proto file.
Traits carry the same identity shape as a vservice — a canonical name used as
the entitlement key plus a stable TokenEntitlements field id — and are parsed
into the same descriptor fields.
-
namestring=1name is the canonical trait name used as the entitlement key (e.g., "query").
-
idint32=2id is the stable field number in TokenEntitlements for this trait.
-
hoststring=3host is the routing label of the physical service that implements this
trait's RPCs. It becomes the (spec.service_s2s)/(spec.service_c2s) label on
the emitted sibling services, so the gateway dials the host while callers
are still entitled againstname— the audience is the contract, not the
implementor. -
s2s_servicestring=4s2s_service and c2s_service name the sibling services the trait's plane is
emitted as. A service may declare only one caller surface
(ValidateServiceSurfaces), so one plane serving both peers and users is two
services. They are declared rather than derived fromnameso the emitted
identities are readable in the declaration instead of in generator source.s2s_service is required. c2s_service is OPTIONAL: leave it empty and the
trait hosts the peer half only — no client-facing sibling is emitted and the
plane contributes no grants to the c2s catalog. A service that wants its own
records readable by users declares (spec.record_read).c2s, which gives it a
scoped <Svc>Records plane instead of a share in this fleet-wide one. -
c2s_servicestring=5 -
auth_servicesrepeatedstring=6auth_services lists the peer identities allowed to call the S2S half, the
per-RPC (spec.rpc).auth_services the emitted service carries. Declared here
rather than hardcoded in the generator: the platform generator must not
name an application service.
ServiceAnnotation¶
message
ServiceAnnotation declares the owning service identity for a proto file.
-
namestring=1name is the canonical service name (e.g. "identity", "gateway").
-
storebool=3store declares this service as a backing store.
Other services reference it via StateAnnotation.label. -
config_storebool=6config_store declares this service as THE backing store for auto-injected
per-service config state (the <Service>ConfigState messages tacplus injects
from (spec.grpc_config)). At most one service in a proto set may set it.
Distinct from store: store says "this service can back state", config_store
says which one config is routed to. Without it, a proto set with more than
one store has no non-arbitrary answer. -
gatewaybool=4gateway declares this service as a gateway (reverse proxy).
Changes TLS source to GatewayTlsState (public cert) instead of X509Mint (internal CA).
The generated run() watches GatewayTlsState for cert hot-reload instead of running
a TLS renewal loop. -
rest_dispatchbool=5rest_dispatch generates REST→gRPC dispatch code for all C2S RPCs visible
to this service. The service acts as a reverse proxy, translating HTTP/JSON
requests into typed gRPC calls. Used by gateway and the CLI. -
accepted_audiencesrepeatedstring=7accepted_audiences widens the set of token
audvalues this service's
generated Authorizer accepts, beyond the two it already derives: its own
service label, plus every vservice/trait it hosts (AcceptedAudiencesForHost).
Omitting it — which every service does today — leaves that derived set
exactly as it is, so this field is backward compatible by construction.Declaring an audience here does NOT grant anything. It only stops the
Authorizer rejecting a token that was correctly minted for that audience;
the caller must still be entitled to the RPC. Who may add an entry is not a
runtime question: this is a proto annotation under VCS and code review, the
same trust boundary as (spec.trait) or an entitlement declaration. -
tenant_ownedbool=8tenant_owned declares this service as a per-tenant instance: the operator
runs one deployment per tenant, and the service's SPIFFE identity is
spiffe://<td>/tenants/<tenant>/services/<name> instead of the shared
/system/services/<name> every other service gets. Tenant identity comes
ONLY from the caller's token namespace at runtime — this annotation does
not carry a tenant, it only declares that the service is instantiated
per-tenant. Read by the operator (per-tenant Deployments), the gateway
(route by token namespace), and the record-plane store wrapper (tenant
pin on S2S reads). Default false — every service today is shared. -
pinned_tenantstring=9pinned_tenant pins a tenant-owned service to exactly one tenant. Meaningful
ONLY alongside tenant_owned = true: when set, the operator instantiates
exactly one instance of the service, in that tenant, and does NOT loop over
the configured tenants. Identity is unchanged — the instance is still
spiffe://<td>/tenants/<pinned_tenant>/services/<name>; only the cardinality
changes. This is for a fleet-singleton service that lives in a reserved
tenant (one row per person, not one row per company), where per-tenant
replication would split the rows the service exists to hold. The operator
FAILS reconciliation if the named tenant is not configured: a pin to a
tenant that does not exist is a config error, not an instance to skip
silently. Empty — the default, and what every service today carries —
leaves tenant_owned's per-tenant replication exactly as it is.
GrpcConfigAnnotation¶
message
GrpcConfigAnnotation marks a message as a gRPC service's configuration.
tacplus uses these to auto-generate a generated.Services message that aggregates all
gRPC configs, plus per-service config state boundaries.
-
service_namestring=1service_name is the gRPC service name.
ConfigSliceAnnotation¶
message
ConfigSliceAnnotation declares one per-service config slice and its owner.
scopeConfigSliceScope=1service_namestring=2
StateImport¶
message
StateImport is the legacy file-level mirror for field-level state_import
declarations. The target state must have export=true on its service_state.
-
sourcestring=1source is "package.StateName" (e.g. "identity.Identity", "aegis.AegisState").
OpaEvalAnnotation¶
message
OpaEvalAnnotation marks a message as an OPA policy evaluation input.
tacplus generates a typed RPC on the opa service for each annotated message.
The input message defines the evaluation parameters; the result message defines
the typed output. Both must be in the same proto file.
Example:
message EntitlementEvalInput {
option (spec.opa_eval) = {
entry_point: "data.entitlements.profiles"
result: "EntitlementEvalResult"
};
string email = 1;
repeated string claims = 2;
}
-
entry_pointstring=1entry_point is the OPA policy path (e.g. "data.entitlements.profiles").
-
resultstring=2result is the name of the result message in the same proto file.
OpaImport¶
message
OpaImport declares a dependency on an OPA eval definition.
Used as a file-level option so each proto package can declare which OPA
evaluations it needs. tacplus generates a typed OpaEval client targeting
the named physical service.
Example:
-
sourcestring=1source is "package.InputMessageName" (e.g. "userentitlements.EntitlementEvalInput").
-
servicestring=2service is the physical service that implements the OpaEval virtual service.
Required. Build fails if the target does not import sys/opalib.proto.
GrpcS2SClient¶
message
GrpcS2SClient declares a service-to-service gRPC client dependency.
Used as a file-level option to declare which S2S gRPC services this service calls.
Proto package names are globally unique, so no file path is needed.
tacplus resolves the package name to its proto file at build time.
Example: option (spec.grpc_s2s_client) = {service: "identity.Mint"};
-
servicestring=1service is "package.ServiceName" (e.g. "identity.Mint").
RecordRead¶
message
RecordRead declares that this service reads a (spec.record) type's STORED
ROWS through the record query plane's S2S half.
The USE half of the paired control whose grant half is
(spec.record).s2s_read. See spec.record_read in the FileOptions block above
for what it generates and why the transport dependency is implied rather than
restated.
-
recordstring=1record is the fully-qualified record message name, "package.Message"
(e.g. "messaging.Message"). Proto package names are globally unique, so no
file path is needed; tacplus resolves the name to its declaring file.A name that resolves to no message, or to a message carrying no
(spec.record), is a hard generation failure — the same fail-loud posture
(spec.record).key already takes for a key naming a field that does not
exist. -
c2sRecordReadC2S=2c2s, when present, asks for a CLIENT-FACING read surface over this record's
rows, emitted into THIS service's own proto as a synthesized sibling
service (<Service>Records) carrying Get<T> and List<T>.Absent — which is every declaration that predates this field — the reader
gets the s2s typed reader and nothing else, byte for byte as before.The grant half is (spec.record).c2s_read on the record, and it must name
this service. See the C2SScope comments for what the emitted surface is
allowed to return.
RecordReadC2S¶
message
RecordReadC2S is the USE half of the client-facing read plane: what
(spec.record_read).c2s asks tacplus to emit.
-
scopeC2SScope=1scope names which rows the surface may return. See C2SScope.
-
exposureRpcExposure=2exposure is stamped onto the emitted RPCs' (spec.rpc). INTERNAL (the
default) keeps the surface inside the cluster boundary; EXTERNAL publishes
it through the gateway, which is what a mobile or browser client needs.
GrpcC2SClient¶
message
GrpcC2SClient declares a client-facing service dependency.
Used as a file-level option to declare which services this package calls.
tacplus.yaml selects whether that dependency emits C2S, W2S, or both.
Example: option (spec.grpc_c2s_client) = {service: "gateway.TokenMint"};
-
servicestring=1service is "package.ServiceName" (e.g. "gateway.TokenMint").
GrpcW2SClient¶
message
GrpcW2SClient declares a client-facing service dependency.
Used as a file-level option to declare which services this package calls.
tacplus.yaml selects whether that dependency emits C2S, W2S, or both.
Example: option (spec.grpc_w2s_client) = {service: "dashboard.Dashboard"};
-
servicestring=1service is "package.ServiceName" (e.g. "dashboard.Dashboard").
RestMethods¶
message
RestMethods is a wrapper for repeated RestMethod, used in method options.
methodsrepeatedRestMethod=1
RestBrowser¶
message
RestBrowser makes an RPC's REST route usable as a browser form target, such
as the callback an identity provider form-posts to (Sign in with Apple's
response_mode=form_post). Only on an RPC with a REST route; unary only.
-
form_bodybool=1form_body accepts an application/x-www-form-urlencoded request body. Each
pair is read as a JSON string value keyed by proto field name, so it fills
the request's string fields. A JSON body is still accepted. A key sent
twice is refused. -
redirect_fieldstring=2redirect_field names a singular string field of the response. The route
answers 303 See Other with Location set to that field's value and
Cache-Control: no-store, instead of the JSON response body.
IpAddrTypes¶
enum
IpAddrTypes defines validation types for IP addresses, masks, and networks.
Used in field options to enforce IP address format validation.
-
IP_ADDR_TYPE_UNSPECIFIED=0IP_ADDR_TYPE_UNSPECIFIED represents an unspecified IP address type
-
IP_ADDR_TYPE_IP_V4=1IP_ADDR_TYPE_IP_V4 validates IPv4 addresses (e.g., 1.1.1.1)
-
IP_ADDR_TYPE_IP_V6=2IP_ADDR_TYPE_IP_V6 validates IPv6 addresses (e.g., 2001:0db8:85a3:0000:0000:8a2e:0370:7334)
-
IP_ADDR_TYPE_IP_ANY=3IP_ADDR_TYPE_IP_ANY validates either IPv4 or IPv6 addresses
-
IP_ADDR_TYPE_MASK_V4=4IP_ADDR_TYPE_MASK_V4 validates IPv4 subnet masks (e.g., 0-32)
-
IP_ADDR_TYPE_MASK_V6=5IP_ADDR_TYPE_MASK_V6 validates IPv6 subnet masks (e.g., 0-128)
-
IP_ADDR_TYPE_MASK_ANY=6IP_ADDR_TYPE_MASK_ANY validates either IPv4 or IPv6 subnet masks
-
IP_ADDR_TYPE_NET_V4=7IP_ADDR_TYPE_NET_V4 validates IPv4 CIDR networks (e.g., 1.1.1.1/32)
-
IP_ADDR_TYPE_NET_V6=8IP_ADDR_TYPE_NET_V6 validates IPv6 CIDR networks (e.g., 2001:0db8:85a3::8a2e:0370:7334/128)
-
IP_ADDR_TYPE_NET_ANY=9IP_ADDR_TYPE_NET_ANY validates either IPv4 or IPv6 CIDR networks
EncType¶
enum
EncType defines validation types for cryptographic keys and certificates.
Used in field options to enforce key format and algorithm validation.
-
ENC_TYPE_KEY_UNDEFINED=0ENC_TYPE_KEY_UNDEFINED represents an unspecified encryption key type
-
ENC_TYPE_KEY_PRIVATE_RSA=1ENC_TYPE_KEY_PRIVATE_RSA validates private RSA keys in PEM format
-
ENC_TYPE_KEY_PUBLIC_RSA=2ENC_TYPE_KEY_PUBLIC_RSA validates public RSA keys in PEM format
-
ENC_TYPE_KEY_ANY_RSA=3ENC_TYPE_KEY_ANY_RSA validates either private or public RSA keys
-
ENC_TYPE_KEY_PRIVATE_EC=4ENC_TYPE_KEY_PRIVATE_EC validates private Elliptic Curve keys in PEM format
-
ENC_TYPE_KEY_PUBLIC_EC=5ENC_TYPE_KEY_PUBLIC_EC validates public Elliptic Curve keys in PEM format
-
ENC_TYPE_KEY_ANY_EC=6ENC_TYPE_KEY_ANY_EC validates either private or public EC keys
-
ENC_TYPE_KEY_PRIVATE_ED25519=7ENC_TYPE_KEY_PRIVATE_ED25519 validates private Ed25519 keys in PEM format
-
ENC_TYPE_KEY_PUBLIC_ED25519=8ENC_TYPE_KEY_PUBLIC_ED25519 validates public Ed25519 keys in PEM format
-
ENC_TYPE_KEY_ANY_ED25519=9ENC_TYPE_KEY_ANY_ED25519 validates either private or public Ed25519 keys
-
ENC_TYPE_KEY_PRIVATE_ANY=10ENC_TYPE_KEY_PRIVATE_ANY validates private keys of any algorithm (RSA, EC, Ed25519)
-
ENC_TYPE_KEY_PUBLIC_ANY=11ENC_TYPE_KEY_PUBLIC_ANY validates public keys of any algorithm
-
ENC_TYPE_KEY_ANY=12ENC_TYPE_KEY_ANY validates any key (private or public) of any algorithm
-
ENC_TYPE_X509_CERT_RSA=13ENC_TYPE_X509_CERT_RSA validates X.509 certificates with RSA public keys
-
ENC_TYPE_X509_CERT_EC=14ENC_TYPE_X509_CERT_EC validates X.509 certificates with EC public keys
-
ENC_TYPE_X509_CERT_ED25519=15ENC_TYPE_X509_CERT_ED25519 validates X.509 certificates with Ed25519 public keys
-
ENC_TYPE_X509_CERT_ANY=16ENC_TYPE_X509_CERT_ANY validates X.509 certificates with any key algorithm
UrlScheme¶
enum
URL_SCHEME_UNSPECIFIED=0-
URL_SCHEME_HTTP=1Allow only http://
-
URL_SCHEME_HTTPS=2Allow only https://
-
URL_SCHEME_HTTP_OR_HTTPS=3Allow both http:// and https://
SqlPermit¶
enum
SqlPermit defines validation types for SQL statement fields.
Used in field options to enforce a syntactic sanity check plus a statement-type restriction.
This is NOT an injection defense: the validator only rejects a handful of obvious tokens
(semicolons, -- and /* comments, backslash escapes) and checks the leading keyword. A
syntactically valid statement can still carry an injection payload (e.g. a UNION or a
subquery in a permitted SELECT), and the check does not cover all comment syntaxes (e.g.
'#' line comments). Callers MUST use parameterized queries and MUST NOT interpolate a validated
string into a query as though it were safe.
-
SQL_PERMIT_UNSPECIFIED=0SQL_PERMIT_UNSPECIFIED represents an unspecified SQL permit type
-
SQL_PERMIT_SELECT=1SQL_PERMIT_SELECT allows only SELECT statements (including WITH ... SELECT CTEs)
-
SQL_PERMIT_DML=2SQL_PERMIT_DML allows SELECT, INSERT, UPDATE, DELETE statements
-
SQL_PERMIT_DDL=3SQL_PERMIT_DDL allows CREATE, ALTER, DROP, TRUNCATE statements
-
SQL_PERMIT_ANY=4SQL_PERMIT_ANY applies only the syntactic sanity check, with no statement-type restriction
DurationType¶
enum
DURATION_TYPE_UNSPECIFIED=0-
DURATION_TYPE_POSITIVE=1> 0
-
DURATION_TYPE_NON_NEGATIVE=2>= 0
PathType¶
enum
PATH_UNSPECIFIED=0-
PATH_ABSOLUTE=1Absolute file path
-
PATH_RELATIVE=2Relative file path
-
PATH_ANY=3Either absolute or relative
-
PATH_DIR=4Directory path (must exist and be a directory)
Azp¶
enum
Azp enumerates the authorized-party / attestation contexts a kernel-minted
token may record (SPIFFE plan D4). It is the proto-defined source of truth for
the azp claim — generated code carries no hardcoded azp list. azp is the
attestation path, NOT part of the subject identity, and kernel sets it from
trusted context (never copied from untrusted request claims).
AZP_UNSPECIFIED=0-
AZP_KERNEL=1kernel: minted by kernel from trusted internal context (service/bootstrap).
The name is a well-known convention from the first platform, not a
dependency on a service called kernel: see docs/USER.md "Well-Known
Names" and https://github.com/tacplus-dev/tacplus/issues/1. -
AZP_OAUTH=2oauth: user attested via an OAuth broker; kernel maps the broker's
authenticated S2S identity to this value at MintUserToken. -
AZP_CLOUD_AUTH=4cloud-auth: reserved for the future cloud-auth attestation broker. No
runtime mints this except tests until that broker exists (D4).
TypeKind¶
enum
TypeKind classifies protobuf field kinds for generated registry metadata.
TYPE_KIND_UNSPECIFIED=0TYPE_KIND_STRING=1TYPE_KIND_BOOL=2TYPE_KIND_BYTES=3TYPE_KIND_INT32=4TYPE_KIND_INT64=5TYPE_KIND_UINT32=6TYPE_KIND_UINT64=7TYPE_KIND_SINT32=8TYPE_KIND_SINT64=9TYPE_KIND_FIXED32=10TYPE_KIND_FIXED64=11TYPE_KIND_SFIXED32=12TYPE_KIND_SFIXED64=13TYPE_KIND_FLOAT=14TYPE_KIND_DOUBLE=15TYPE_KIND_ENUM=16TYPE_KIND_MESSAGE=17TYPE_KIND_MAP=18
PrincipalScope¶
enum
PrincipalScope is how wide a caller's authority over PRINCIPALS is. It rides
the caller's grant as the "principal_scope" key of the OPA-stamped
service_entitlements claim bag — the same bag the egress "data_grants" ride —
so it is one more claim on an existing seam, not a second auth path.
The generated service wrapper reads it by name and compares it to the row's
principal. SELF_ONLY (and absent/unspecified, which fail closed to it) means
the caller may act only as its own principal, which is what scopes a
(spec.owner_default) refusal to the row's owner instead of to every caller.
Anything wider means the caller is not the owner acting on itself.
SELF_ONLY, not SELF: the Rust variant is the value name with the enum prefix
stripped, and Self is a Rust keyword that protoc-gen-prost-serde does not
escape (prost renders Self_, the serde companion renders Self, and the
crate does not compile). See PRINCIPAL_SCOPE_QUESTIONS.md.
-
PRINCIPAL_SCOPE_UNSPECIFIED=0Unspecified is the proto3 zero value and is treated as SELF_ONLY: a token
that says nothing about principal scope has not been widened. -
PRINCIPAL_SCOPE_SELF_ONLY=1Self only: the caller may act only on rows whose principal is the caller.
-
PRINCIPAL_SCOPE_SUBTREE=2Subtree: the caller may act on rows whose principal is at or below the
caller's own namespace. -
PRINCIPAL_SCOPE_ANY=3Any: the caller may act on any principal's rows.
Mutability¶
enum
Mutability is whether a stored record type's rows may change after write.
-
MUTABILITY_UNSPECIFIED=0Unspecified = MUTABILITY_CRUD (today's behaviour).
-
MUTABILITY_CRUD=1CRUD: rows may be updated and deleted by key.
-
MUTABILITY_APPEND_ONLY=2APPEND_ONLY: rows are written once. No row-mutating RPC is generated for
the type and the generated wrapper refuses to host one. Its disk slices are
pure appends — sealed, checksummable units.
CardinalityHint¶
enum
CardinalityHint is a store-neutral hint about how many distinct values a
column holds. A downstream materializer may use it to pick an encoding, but
tacplus emits no concrete codec — the hint is pure semantic intent.
CARDINALITY_UNSPECIFIED=0-
CARDINALITY_LOW=1few distinct values; was low_cardinality=true
-
CARDINALITY_HIGH=2many distinct values
DedupStrategy¶
enum
DedupStrategy is a store-neutral statement of how duplicate rows are resolved.
-
DEDUP_NONE=0append-only; keep all rows
-
DEDUP_LATEST_BY_VERSION=1keep latest row per key by version_field
SmbRoot¶
enum
SmbRoot selects which trust root backs an SMB service's member certificates.
-
SMB_ROOT_ISOLATED=0SMB_ROOT_ISOLATED uses a self-signed per-SMB CA, scoped only to this SMB.
-
SMB_ROOT_CLUSTER=1SMB_ROOT_CLUSTER signs the per-SMB intermediate under the cluster CA.
RpcPrincipal¶
enum
RpcPrincipal declares WHO calls an RPC: a user identity, a service identity,
or a model acting through a generated MCP tool surface. They are mutually
exclusive, so this is an enum rather than a flag. Every spec.rpc must set it —
there is no safe default, because either wrong answer is a silent
authorization bug rather than a loud failure.
RPC_PRINCIPAL_UNSPECIFIED=0-
RPC_PRINCIPAL_USER=1RPC_PRINCIPAL_USER: the caller acts as a user identity. Internally that is
a client calling as a given user; externally it is the actual user. -
RPC_PRINCIPAL_SERVICE=2RPC_PRINCIPAL_SERVICE: the caller acts as a service identity.
-
RPC_PRINCIPAL_MCP=3RPC_PRINCIPAL_MCP: this RPC is declared as an MCP tool rather than a gRPC
call. tacplus generates the tool schema and a handler interface method the
service implements by hand; it maps the tool to no gRPC RPC of its own.
Being a value of this enum rather than a flag is what keeps a DECLARED tool
from also being a gRPC call: an rpc block holds one principal.It is NOT how most tools come to exist. Every user RPC of a service_c2s
service — not RPC_PRINCIPAL_SERVICE, minus RPC_FLAG_PUBLIC_NO_AUTH and
RPC_FLAG_SELF_AUTHENTICATES — is published as a tool automatically, with no
annotation asking for one and with its gRPC call, REST route and
entitlement grant intact. Declare this principal only for a tool that has
no gRPC rpc of its own. Authorization is unchanged either way — flags /
auth_services / target_namespace_field mean the same thing against whatever
Caller the MCP session captured at handshake as they do against a gRPC
caller.Streaming is a hard generation failure on this principal: the tool-call wire
format has no representation for a stream. A handler is free to drive a
Watch internally and fold it into one response.
RpcExposure¶
enum
RpcExposure declares WHERE an RPC is reachable from. Also mutually
exclusive, so also an enum. Unlike principal it has a safe default:
forgetting it under-exposes the RPC, which fails loudly at call time rather
than silently widening the surface.
-
RPC_EXPOSURE_INTERNAL=0RPC_EXPOSURE_INTERNAL: reachable only from inside the cluster boundary.
-
RPC_EXPOSURE_EXTERNAL=1RPC_EXPOSURE_EXTERNAL: reachable from outside the cluster boundary.
RpcFlag¶
enum
RpcFlag carries the non-exclusive protocol properties of an RPC — the
things that are independent of principal and exposure and of each other, so
an RPC may carry any combination of them (including none).
RPC_FLAG_UNSPECIFIED=0-
RPC_FLAG_PUBLIC_NO_AUTH=1RPC_FLAG_PUBLIC_NO_AUTH: this RPC is a public endpoint that requires no
caller token at all (OIDC discovery, JWKS). Nothing authenticates the
caller, because there is nothing to authenticate. -
RPC_FLAG_AUTH_ANY_USER=2RPC_FLAG_AUTH_ANY_USER: any authenticated user may call, with no
entitlement check (e.g. token exchange, event streams). -
RPC_FLAG_AUTH_ENTITLED_USERS=3RPC_FLAG_AUTH_ENTITLED_USERS: only users holding a matching entitlement
may call. This is today's default policy for user-facing RPCs. -
RPC_FLAG_SELF_AUTHENTICATES=4RPC_FLAG_SELF_AUTHENTICATES: this RPC carries no bearer token because it
authenticates the caller from the request body itself (bootstrap token
minting, cred-pair JWT). The caller interceptor skips it; the handler does
the authentication. Distinct from RPC_FLAG_PUBLIC_NO_AUTH: the caller is
still authenticated, just not by the token path. -
RPC_FLAG_MINTS_CREDENTIAL=5RPC_FLAG_MINTS_CREDENTIAL: this RPC's response mints or returns a live
credential — a token, a certificate, a delegate grant — so it is never
auto-exposed as an MCP tool, whatever its principal. It carries no
auth/authorizer semantics of its own: a mint RPC's authorization is
already fully expressed by its principal, its other flags and its
auth_services. This flag only affects MCP auto-exposure.
CollectionMode¶
enum
CollectionMode declares how a multi-result read behaves. See docs/API.md.
A read returning more than one result must declare exactly one mode; an
unbounded SNAPSHOT is a contradiction (if it is unbounded, it is a WATCH).
COLLECTION_MODE_UNSPECIFIED=0-
COLLECTION_MODE_SNAPSHOT=1SNAPSHOT: a finite point-in-time set that terminates. Requires max_items.
tacplus derives the delivery tier (inline / accumulated-stream / cursor)
from max_items and the response's max element size. -
COLLECTION_MODE_WATCH=2WATCH: an open-ended, change-driven stream (snapshot-then-deltas). Forbids
max_items; uses a bounded replay ring (replay_depth) for resume. -
COLLECTION_MODE_PAGE=3PAGE: a client-driven, resumable, windowed GET (unary). The caller pages
through a finite set with an opaque keyset page_token over order_by; each
page is bounded by page_size (capped at the trust-tier ceiling). Forbids
max_items/replay_depth. See the archived RPC_PATTERNS_AND_VERBS_PLAN plan §4.
Visibility¶
enum
Visibility is the (spec.visibility) value of a public_view message's field.
-
VISIBILITY_UNSPECIFIED=0invalid — the annotation must name a side
-
VISIBILITY_SHOW=1may be rendered on the record's public page
-
VISIBILITY_BACKGROUND=2never leaves the company; absent from <T>Public
QueryOp¶
enum
QueryOp is the match kind for a (spec.query_field) record filter criterion.
-
QUERY_OP_UNSPECIFIED=0treated as EQ
-
QUERY_OP_EQ=1equality: optional <type> <name>
-
QUERY_OP_RANGE=2inclusive range: optional <type> <name>_from/_to
RedactMode¶
enum
RedactMode selects the data-entitlement transform for a field on its way out
of a service (egress-only, e.g. Sample/Pull). It expresses the four states of
the data-entitlement lattice across two transform kinds (redact, anonymize)
and two floors (hard "is-*", eligible "may-*"). Effective level per field is
max(annotation floor, runtime entitlement) under pass-through < anonymize <
redact — the runtime entitlement can only ratchet tighter, never loosen below
the floor. The two "is-*" modes are the original unconditional behavior.
-
REDACT_MODE_UNSPECIFIED=0invalid — presence of the annotation implies a real mode
-
REDACT_MODE_DROP=1HARD FLOOR redact: always cleared to zero (is-redacted)
-
REDACT_MODE_PSEUDONYMIZE=2HARD FLOOR anonymize: always keyed-HMAC pseudonym (is-anonymized)
-
REDACT_MODE_MAY_REDACT=3ELIGIBLE redact: runtime entitlement decides (may-be-redacted)
-
REDACT_MODE_MAY_ANONYMIZE=4ELIGIBLE anonymize: runtime entitlement decides (may-be-anonymized)
EvictionPolicy¶
enum
EvictionPolicy determines which entries to evict when max_size is reached.
Used as a field-level annotation on boundary fields and as a message-level
default on state root messages.
-
EVICTION_POLICY_NONE=0EVICTION_POLICY_NONE disables eviction (default). Entries are never evicted by policy.
-
EVICTION_POLICY_LRU=1EVICTION_POLICY_LRU evicts least recently used entries.
-
EVICTION_POLICY_LFU=2EVICTION_POLICY_LFU evicts least frequently used entries.
-
EVICTION_POLICY_RANDOM=3EVICTION_POLICY_RANDOM evicts entries randomly.
MaxSizePolicy¶
enum
MaxSizePolicy determines what the eviction_max_size limit refers to.
-
MAX_SIZE_POLICY_ENTRY_COUNT=0MAX_SIZE_POLICY_ENTRY_COUNT limits by total number of entries (default).
-
MAX_SIZE_POLICY_USED_HEAP_SIZE=1MAX_SIZE_POLICY_USED_HEAP_SIZE limits by heap memory used in bytes.
-
MAX_SIZE_POLICY_USED_HEAP_PERCENTAGE=2MAX_SIZE_POLICY_USED_HEAP_PERCENTAGE limits by percentage of heap memory used.
-
MAX_SIZE_POLICY_FREE_HEAP_PERCENTAGE=3MAX_SIZE_POLICY_FREE_HEAP_PERCENTAGE evicts when free heap drops below percentage.
TlsKeyType¶
enum
TlsKeyType specifies the key algorithm for auto-TLS certificate management.
-
TLS_KEY_TYPE_NONE=0TLS_KEY_TYPE_NONE disables auto-TLS (default).
-
TLS_KEY_TYPE_ED25519=1TLS_KEY_TYPE_ED25519 uses Ed25519 (OKP) keys.
-
TLS_KEY_TYPE_ECDSA=2TLS_KEY_TYPE_ECDSA uses ECDSA P-256 keys.
ConfigSliceScope¶
enum
ConfigSliceScope identifies the aggregate config message that receives a
per-service config slice.
CONFIG_SLICE_SCOPE_UNSPECIFIED=0CONFIG_SLICE_SCOPE_TENANT=1CONFIG_SLICE_SCOPE_PROJECT=2
C2SScope¶
enum
C2SScope names WHICH ROWS a client-facing record read may return, out of a
closed set. It is not a predicate language and must never become one.
Every value here is derivable from VERIFIED TOKEN CLAIMS alone. The moment a
scope would need something else — "the decider this ticket is routed to", a
reach down an org hierarchy — it has stopped being a generated backstop and
become business logic, and such a read belongs in a HAND-WRITTEN RPC whose
body calls the generated s2s reader. That boundary is the design, not a gap
in it.
Declared top-level rather than nested inside RecordReadC2S: a nested enum
generates but does not compile in the Rust model crate.
-
C2S_SCOPE_UNSPECIFIED=0Unspecified = C2S_SCOPE_PRINCIPAL. The narrowest value is the default, so a
reader that declares the block and forgets the scope discloses the least,
not the most. -
C2S_SCOPE_PRINCIPAL=1PRINCIPAL: rows whose owning-principal field equals the caller's subject,
AND whose namespace equals the caller's tenant. "My own rows."The principal field is resolved exactly as the egress row filter resolves
it — (spec.store_principal) on a record field, else (spec.record).owner —
and ONLY when that field is also an EQ criterion on the generated <T>Filter.
When it resolves to nothing, generation FAILS rather than degrading to a
tenant-wide read: on ScottyActivity and AccountingRecord the declared
owneris the PUBLISHING SERVICE, so a filter on it would show a human
every row in its tenant while looking like it showed them their own. -
C2S_SCOPE_NAMESPACE=2NAMESPACE: every row in the caller's tenant, with no principal narrowing.
A legitimate shape — an org-wide audit feed — but a disclosure decision, so
it must be written down and can never be what absence means.DECLARED BUT NOT IMPLEMENTED in v1: generation fails with an explicit
"declared but not implemented" error. The value exists so it cannot be
invented twice with two meanings.
RestMethod¶
enum
RestMethod enumerates additional HTTP methods for REST exposure.
POST is always implicit on every convention REST route and does not
appear in this enum. These values are used with spec.rest_method to
declare additional HTTP methods for an RPC.
REST_METHOD_UNSPECIFIED=0REST_METHOD_GET=1REST_METHOD_PATCH=2REST_METHOD_PUT=3REST_METHOD_DELETE=4