Skip to content

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.

  • name string = 1

    Name is the identifier for the constant

  • value string = 2

    Value 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.

  • name string = 1

    Name is the identifier for the constant

  • value int64 = 2

    Value 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.

  • name string = 1

    Name is the identifier for the constant

  • value double = 2

    Value 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.

  • name string = 1

    Name is the identifier for the constant

  • value bool = 2

    Value 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.

  • host string = 1
  • port int32 = 2

NumericConstraint

message

NumericConstraint defines constraints for integer fields (int32, int64, uint32, uint64)
This message supports complex validation scenarios beyond simple comparisons

  • gt int64 = 1

    gt: field must be greater than this value

  • ge int64 = 2

    ge: field must be greater than or equal to this value

  • lt int64 = 3

    lt: field must be less than this value

  • le int64 = 4

    le: field must be less than or equal to this value

  • eq int64 = 5

    eq: field must equal this value

  • ne int64 = 6

    ne: field must not equal this value

  • in repeated int64 = 7

    in: field must be one of these values

  • not_in repeated int64 = 8

    not_in: field must not be one of these values

  • multiple_of int64 = 9

    multiple_of: field must be divisible by this value (field % multiple_of == 0)

FloatConstraint

message

FloatConstraint defines constraints for floating-point fields (float, double)

  • gt double = 1

    gt: field must be greater than this value

  • ge double = 2

    ge: field must be greater than or equal to this value

  • lt double = 3

    lt: field must be less than this value

  • le double = 4

    le: field must be less than or equal to this value

  • epsilon double = 5

    epsilon: tolerance for floating-point equality comparisons (default: 1e-9)
    Used when comparing floats to avoid precision issues

  • in repeated double = 6

    in: field must be one of these values (within epsilon)

  • not_in repeated double = 7

    not_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_enum string = 1

    The enum type to use for parameter keys (e.g., "TokenParamKey")
    The enum must have json_return_value annotations on its values

  • strict bool = 2

    Whether to return an error on unknown keys (default: false, ignore unknown)

  • validate_required bool = 3

    Whether 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.

  • read bool = 1

    read: the owner may read rows of this type.

  • write bool = 2

    write: 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.

  • source string = 1

    source 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.

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.

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).

  • key string = 1

    key is the proto field name used as the unique record identifier.

  • owner string = 2

    owner is the proto field name that identifies the record's owner (e.g. spiffe_id).

  • c2s MethodC2SAnnotation = 3

    c2s, 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.

  • s2s MethodS2SAnnotation = 4

    s2s, 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_size int32 = 6

    min_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.

  • residency ResidencyPolicy = 7

    residency 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:

    option (spec.record) = {
      key: "accounting_id" owner: "spiffe_id"
      residency: {idle_seconds: 3600 deleted_max_age_seconds: 2592000}
    };
    
  • mutability Mutability = 8

    mutability 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_subscribe MethodS2SAnnotation = 9

    s2s_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:

    option (spec.record) = {
      key: "accounting_id" owner: "spiffe_id"
      s2s: {auth_services: ["*"]}              // anyone may publish
      s2s_subscribe: {auth_services: ["recordstore"]}  // only the store may read
    };
    
  • s2s_read MethodS2SAnnotation = 10

    s2s_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 shared query trait 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_read MethodS2SAnnotation = 11

    c2s_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:

    option (spec.record) = {
      key: "activity_id" owner: "spiffe_id"
      s2s:           {auth_services: ["assistant"]}
      s2s_subscribe: {auth_services: ["recordstore"]}
      s2s_read:      {auth_services: ["assistant"]}
      c2s_read:      {auth_services: ["assistant"]}
    };
    
  • private bool = 12

    private 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:

    option (spec.record) = {
      key: "rule_id" owner: "spiffe_id"
      s2s: {auth_services: ["profile"]}
      private: true
    };
    

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_seconds int64 = 1

    idle_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_seconds int64 = 2

    deleted_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).

  • c2s MethodC2SAnnotation = 1

    c2s, when present, exposes the publish RPC to external clients (via gateway).

  • s2s MethodS2SAnnotation = 2

    s2s, when present, exposes the publish RPC to internal services.
    Producer surface only; the consumer surface is s2s_subscribe.

  • s2s_subscribe MethodS2SAnnotation = 3

    s2s_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.

  • strategy DedupStrategy = 1
  • key_fields repeated string = 2

    logical row identity

  • version_field string = 3

    selects 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.

  • name string = 1

    name is the derived row type name (e.g. "SessionRecord").

  • when string = 4

    when is the oneof variant name that triggers this row (e.g. "session").
    Only source messages carrying this payload variant produce a row.

  • field repeated TransformationField = 5

    field lists the source fields projected into the derived type.

  • dedup DedupPolicy = 7

    dedup is the store-neutral duplicate-resolution policy for the derived rows.

TransformationField

message

TransformationField maps a source message field to a derived-type column.

  • source string = 1

    source is the field path on the source message (e.g. "session.model").

  • as string = 2

    as renames the column in the derived type (defaults to last segment of source).

  • order_key int32 = 3

    order_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_key bool = 4

    partition_key marks this column as part of the store-neutral partition role.

  • cardinality_hint CardinalityHint = 6

    cardinality_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:

message SensorTimeseries {
  option (spec.timeseries_type) = {source: "sensor.SensorEvent"};
  ...
}

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.

  • name string = 1

    Table name. Default: snake_case of message name.

  • source string = 4

    Source 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"

  • dedup DedupPolicy = 6

    dedup 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_key int32 = 3

    Position in the store-neutral sort-key role (1-indexed). 0 = not a sort key.

  • partition_key bool = 4

    Part of the store-neutral partition role.

  • cardinality_hint CardinalityHint = 7

    Store-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_services repeated string = 1

    auth_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.

  • label string = 3

    label 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_users bool = 3

    auth_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.

  • label string = 4

    label 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_path string = 1

    All 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_field string = 1

    namespace_field is the sibling field holding the namespace path.

  • kind string = 2

    kind is the resource kind segment (a constant, validated as a single segment).

  • id_field string = 3

    id_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.

  • proxy bool = 1

    proxy 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.

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.

  • name string = 1

    name is the volume group's identifier. Must be unique across the whole fleet.

  • max_blob_bytes uint64 = 2

    max_blob_bytes is the largest single blob this group accepts.

  • content_types repeated string = 3

    content_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_bytes uint64 = 4

    tenant_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_services repeated string = 1

    auth_services overrides the service-level default for this method.

  • auth_disabled bool = 2

    auth_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_services repeated string = 3

    routing_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_disabled bool = 1

    auth_disabled: no token required for this RPC.
    Used for public endpoints like OIDC discovery and JWKS.

  • auth_any_user bool = 2

    auth_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_field string = 5

    target_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's namespace claim) 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]
};
  • principal RpcPrincipal = 1

    principal is required; RPC_PRINCIPAL_UNSPECIFIED is not a valid declaration.

  • exposure RpcExposure = 2

    exposure defaults to RPC_EXPOSURE_INTERNAL.

  • flags repeated RpcFlag = 3

    flags is an unordered set; RPC_FLAG_UNSPECIFIED is not a valid entry.

  • auth_services repeated string = 4

    auth_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_field string = 5

    target_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's namespace claim) 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_name string = 6

    mcp_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_actors repeated string = 7

    auth_actors lists the delegate identities allowed to call this RPC on
    another principal's behalf. Entries are matched against the token's act
    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 carries act: a token with no act claim is authorized
    exactly as it is today. A token that DOES carry act is 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_device bool = 8

    requires_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-id and
    x-tacplus-device-assertion-bin off 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_when RequiresDeviceWhen = 9

    requires_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.

  • field string = 1

    field 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.

  • equals string = 2

    equals 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.

  • mode CollectionMode = 1

    mode is required: SNAPSHOT (finite) or WATCH (open-ended).

  • order_by repeated string = 2

    order_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_items uint32 = 3

    max_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_depth uint32 = 4

    replay_depth is the WATCH replay-ring depth (count of retained changes).
    Optional; defaults to a platform constant. Forbidden on SNAPSHOT.

  • default_page_size uint32 = 5

    default_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_field string = 6

    revision_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 carries seq, per-owner monotonic, and is
    already the keyset ListChatEvents pages over. Forcing a second int64
    called revision onto 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).

  • label string = 1

    label is the routing label for service resolution (e.g. "eventbus").

  • export bool = 3

    export 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.

  • field string = 1

    field is the sibling field name (snake_case) to check

  • value string = 2

    value is the enum value name or string to compare against

QueryFieldOption

message

QueryFieldOption configures a record field as a RecordMap query filter criterion.

RedactOption

message

RedactOption configures a field (or, at message level via (spec.data_entitlement),
a whole message default) with a data-entitlement mode.

StateAnnotation

message

StateAnnotation marks a message as a state root and references its backing store.

  • name string = 1

    name is the state root name (e.g. "ClusterState", "AegisState").

  • label repeated string = 3

    label 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_tls TlsKeyType = 4

    auto_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.

  • export bool = 5

    export makes this state root available for RO import by other services.
    Without export, the state is private to the owning service.

  • requires_artifact_on_write bool = 6

    requires_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_snapshot bool = 7

    identity_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.

  • label repeated string = 1

    label 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).

  • name string = 1

    name is the canonical vservice name used as the entitlement key (e.g., "telemetry").

  • id int32 = 2

    id 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.

  • name string = 1

    name is the canonical trait name used as the entitlement key (e.g., "query").

  • id int32 = 2

    id is the stable field number in TokenEntitlements for this trait.

  • host string = 3

    host 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 against name — the audience is the contract, not the
    implementor.

  • s2s_service string = 4

    s2s_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 from name so 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_service string = 5

  • auth_services repeated string = 6

    auth_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.

  • name string = 1

    name is the canonical service name (e.g. "identity", "gateway").

  • store bool = 3

    store declares this service as a backing store.
    Other services reference it via StateAnnotation.label.

  • config_store bool = 6

    config_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.

  • gateway bool = 4

    gateway 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_dispatch bool = 5

    rest_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_audiences repeated string = 7

    accepted_audiences widens the set of token aud values 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_owned bool = 8

    tenant_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_tenant string = 9

    pinned_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_name string = 1

    service_name is the gRPC service name.

ConfigSliceAnnotation

message

ConfigSliceAnnotation declares one per-service config slice and its owner.

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.

  • source string = 1

    source 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_point string = 1

    entry_point is the OPA policy path (e.g. "data.entitlements.profiles").

  • result string = 2

    result 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:

option (spec.opa_import) = {source: "userentitlements.EntitlementEvalInput", service: "opa"};
  • source string = 1

    source is "package.InputMessageName" (e.g. "userentitlements.EntitlementEvalInput").

  • service string = 2

    service 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"};

  • service string = 1

    service 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.

  • record string = 1

    record 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.

  • c2s RecordReadC2S = 2

    c2s, 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.

  • scope C2SScope = 1

    scope names which rows the surface may return. See C2SScope.

  • exposure RpcExposure = 2

    exposure 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"};

  • service string = 1

    service 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"};

  • service string = 1

    service is "package.ServiceName" (e.g. "dashboard.Dashboard").

RestMethods

message

RestMethods is a wrapper for repeated RestMethod, used in method options.

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_body bool = 1

    form_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_field string = 2

    redirect_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 = 0

    IP_ADDR_TYPE_UNSPECIFIED represents an unspecified IP address type

  • IP_ADDR_TYPE_IP_V4 = 1

    IP_ADDR_TYPE_IP_V4 validates IPv4 addresses (e.g., 1.1.1.1)

  • IP_ADDR_TYPE_IP_V6 = 2

    IP_ADDR_TYPE_IP_V6 validates IPv6 addresses (e.g., 2001:0db8:85a3:0000:0000:8a2e:0370:7334)

  • IP_ADDR_TYPE_IP_ANY = 3

    IP_ADDR_TYPE_IP_ANY validates either IPv4 or IPv6 addresses

  • IP_ADDR_TYPE_MASK_V4 = 4

    IP_ADDR_TYPE_MASK_V4 validates IPv4 subnet masks (e.g., 0-32)

  • IP_ADDR_TYPE_MASK_V6 = 5

    IP_ADDR_TYPE_MASK_V6 validates IPv6 subnet masks (e.g., 0-128)

  • IP_ADDR_TYPE_MASK_ANY = 6

    IP_ADDR_TYPE_MASK_ANY validates either IPv4 or IPv6 subnet masks

  • IP_ADDR_TYPE_NET_V4 = 7

    IP_ADDR_TYPE_NET_V4 validates IPv4 CIDR networks (e.g., 1.1.1.1/32)

  • IP_ADDR_TYPE_NET_V6 = 8

    IP_ADDR_TYPE_NET_V6 validates IPv6 CIDR networks (e.g., 2001:0db8:85a3::8a2e:0370:7334/128)

  • IP_ADDR_TYPE_NET_ANY = 9

    IP_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 = 0

    ENC_TYPE_KEY_UNDEFINED represents an unspecified encryption key type

  • ENC_TYPE_KEY_PRIVATE_RSA = 1

    ENC_TYPE_KEY_PRIVATE_RSA validates private RSA keys in PEM format

  • ENC_TYPE_KEY_PUBLIC_RSA = 2

    ENC_TYPE_KEY_PUBLIC_RSA validates public RSA keys in PEM format

  • ENC_TYPE_KEY_ANY_RSA = 3

    ENC_TYPE_KEY_ANY_RSA validates either private or public RSA keys

  • ENC_TYPE_KEY_PRIVATE_EC = 4

    ENC_TYPE_KEY_PRIVATE_EC validates private Elliptic Curve keys in PEM format

  • ENC_TYPE_KEY_PUBLIC_EC = 5

    ENC_TYPE_KEY_PUBLIC_EC validates public Elliptic Curve keys in PEM format

  • ENC_TYPE_KEY_ANY_EC = 6

    ENC_TYPE_KEY_ANY_EC validates either private or public EC keys

  • ENC_TYPE_KEY_PRIVATE_ED25519 = 7

    ENC_TYPE_KEY_PRIVATE_ED25519 validates private Ed25519 keys in PEM format

  • ENC_TYPE_KEY_PUBLIC_ED25519 = 8

    ENC_TYPE_KEY_PUBLIC_ED25519 validates public Ed25519 keys in PEM format

  • ENC_TYPE_KEY_ANY_ED25519 = 9

    ENC_TYPE_KEY_ANY_ED25519 validates either private or public Ed25519 keys

  • ENC_TYPE_KEY_PRIVATE_ANY = 10

    ENC_TYPE_KEY_PRIVATE_ANY validates private keys of any algorithm (RSA, EC, Ed25519)

  • ENC_TYPE_KEY_PUBLIC_ANY = 11

    ENC_TYPE_KEY_PUBLIC_ANY validates public keys of any algorithm

  • ENC_TYPE_KEY_ANY = 12

    ENC_TYPE_KEY_ANY validates any key (private or public) of any algorithm

  • ENC_TYPE_X509_CERT_RSA = 13

    ENC_TYPE_X509_CERT_RSA validates X.509 certificates with RSA public keys

  • ENC_TYPE_X509_CERT_EC = 14

    ENC_TYPE_X509_CERT_EC validates X.509 certificates with EC public keys

  • ENC_TYPE_X509_CERT_ED25519 = 15

    ENC_TYPE_X509_CERT_ED25519 validates X.509 certificates with Ed25519 public keys

  • ENC_TYPE_X509_CERT_ANY = 16

    ENC_TYPE_X509_CERT_ANY validates X.509 certificates with any key algorithm

UrlScheme

enum

  • URL_SCHEME_UNSPECIFIED = 0
  • URL_SCHEME_HTTP = 1

    Allow only http://

  • URL_SCHEME_HTTPS = 2

    Allow only https://

  • URL_SCHEME_HTTP_OR_HTTPS = 3

    Allow 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 = 0

    SQL_PERMIT_UNSPECIFIED represents an unspecified SQL permit type

  • SQL_PERMIT_SELECT = 1

    SQL_PERMIT_SELECT allows only SELECT statements (including WITH ... SELECT CTEs)

  • SQL_PERMIT_DML = 2

    SQL_PERMIT_DML allows SELECT, INSERT, UPDATE, DELETE statements

  • SQL_PERMIT_DDL = 3

    SQL_PERMIT_DDL allows CREATE, ALTER, DROP, TRUNCATE statements

  • SQL_PERMIT_ANY = 4

    SQL_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 = 1

    Absolute file path

  • PATH_RELATIVE = 2

    Relative file path

  • PATH_ANY = 3

    Either absolute or relative

  • PATH_DIR = 4

    Directory 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 = 1

    kernel: 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 = 2

    oauth: user attested via an OAuth broker; kernel maps the broker's
    authenticated S2S identity to this value at MintUserToken.

  • AZP_CLOUD_AUTH = 4

    cloud-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 = 0
  • TYPE_KIND_STRING = 1
  • TYPE_KIND_BOOL = 2
  • TYPE_KIND_BYTES = 3
  • TYPE_KIND_INT32 = 4
  • TYPE_KIND_INT64 = 5
  • TYPE_KIND_UINT32 = 6
  • TYPE_KIND_UINT64 = 7
  • TYPE_KIND_SINT32 = 8
  • TYPE_KIND_SINT64 = 9
  • TYPE_KIND_FIXED32 = 10
  • TYPE_KIND_FIXED64 = 11
  • TYPE_KIND_SFIXED32 = 12
  • TYPE_KIND_SFIXED64 = 13
  • TYPE_KIND_FLOAT = 14
  • TYPE_KIND_DOUBLE = 15
  • TYPE_KIND_ENUM = 16
  • TYPE_KIND_MESSAGE = 17
  • TYPE_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 = 0

    Unspecified 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 = 1

    Self only: the caller may act only on rows whose principal is the caller.

  • PRINCIPAL_SCOPE_SUBTREE = 2

    Subtree: the caller may act on rows whose principal is at or below the
    caller's own namespace.

  • PRINCIPAL_SCOPE_ANY = 3

    Any: 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 = 0

    Unspecified = MUTABILITY_CRUD (today's behaviour).

  • MUTABILITY_CRUD = 1

    CRUD: rows may be updated and deleted by key.

  • MUTABILITY_APPEND_ONLY = 2

    APPEND_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 = 1

    few distinct values; was low_cardinality=true

  • CARDINALITY_HIGH = 2

    many distinct values

DedupStrategy

enum

DedupStrategy is a store-neutral statement of how duplicate rows are resolved.

  • DEDUP_NONE = 0

    append-only; keep all rows

  • DEDUP_LATEST_BY_VERSION = 1

    keep latest row per key by version_field

SmbRoot

enum

SmbRoot selects which trust root backs an SMB service's member certificates.

  • SMB_ROOT_ISOLATED = 0

    SMB_ROOT_ISOLATED uses a self-signed per-SMB CA, scoped only to this SMB.

  • SMB_ROOT_CLUSTER = 1

    SMB_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 = 1

    RPC_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 = 2

    RPC_PRINCIPAL_SERVICE: the caller acts as a service identity.

  • RPC_PRINCIPAL_MCP = 3

    RPC_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 = 0

    RPC_EXPOSURE_INTERNAL: reachable only from inside the cluster boundary.

  • RPC_EXPOSURE_EXTERNAL = 1

    RPC_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 = 1

    RPC_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 = 2

    RPC_FLAG_AUTH_ANY_USER: any authenticated user may call, with no
    entitlement check (e.g. token exchange, event streams).

  • RPC_FLAG_AUTH_ENTITLED_USERS = 3

    RPC_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 = 4

    RPC_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 = 5

    RPC_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 = 1

    SNAPSHOT: 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 = 2

    WATCH: an open-ended, change-driven stream (snapshot-then-deltas). Forbids
    max_items; uses a bounded replay ring (replay_depth) for resume.

  • COLLECTION_MODE_PAGE = 3

    PAGE: 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 = 0

    invalid — the annotation must name a side

  • VISIBILITY_SHOW = 1

    may be rendered on the record's public page

  • VISIBILITY_BACKGROUND = 2

    never 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 = 0

    treated as EQ

  • QUERY_OP_EQ = 1

    equality: optional <type> <name>

  • QUERY_OP_RANGE = 2

    inclusive 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 = 0

    invalid — presence of the annotation implies a real mode

  • REDACT_MODE_DROP = 1

    HARD FLOOR redact: always cleared to zero (is-redacted)

  • REDACT_MODE_PSEUDONYMIZE = 2

    HARD FLOOR anonymize: always keyed-HMAC pseudonym (is-anonymized)

  • REDACT_MODE_MAY_REDACT = 3

    ELIGIBLE redact: runtime entitlement decides (may-be-redacted)

  • REDACT_MODE_MAY_ANONYMIZE = 4

    ELIGIBLE 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 = 0

    EVICTION_POLICY_NONE disables eviction (default). Entries are never evicted by policy.

  • EVICTION_POLICY_LRU = 1

    EVICTION_POLICY_LRU evicts least recently used entries.

  • EVICTION_POLICY_LFU = 2

    EVICTION_POLICY_LFU evicts least frequently used entries.

  • EVICTION_POLICY_RANDOM = 3

    EVICTION_POLICY_RANDOM evicts entries randomly.

MaxSizePolicy

enum

MaxSizePolicy determines what the eviction_max_size limit refers to.

  • MAX_SIZE_POLICY_ENTRY_COUNT = 0

    MAX_SIZE_POLICY_ENTRY_COUNT limits by total number of entries (default).

  • MAX_SIZE_POLICY_USED_HEAP_SIZE = 1

    MAX_SIZE_POLICY_USED_HEAP_SIZE limits by heap memory used in bytes.

  • MAX_SIZE_POLICY_USED_HEAP_PERCENTAGE = 2

    MAX_SIZE_POLICY_USED_HEAP_PERCENTAGE limits by percentage of heap memory used.

  • MAX_SIZE_POLICY_FREE_HEAP_PERCENTAGE = 3

    MAX_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 = 0

    TLS_KEY_TYPE_NONE disables auto-TLS (default).

  • TLS_KEY_TYPE_ED25519 = 1

    TLS_KEY_TYPE_ED25519 uses Ed25519 (OKP) keys.

  • TLS_KEY_TYPE_ECDSA = 2

    TLS_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 = 0
  • CONFIG_SLICE_SCOPE_TENANT = 1
  • CONFIG_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 = 0

    Unspecified = 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 = 1

    PRINCIPAL: 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
    owner is 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 = 2

    NAMESPACE: 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 = 0
  • REST_METHOD_GET = 1
  • REST_METHOD_PATCH = 2
  • REST_METHOD_PUT = 3
  • REST_METHOD_DELETE = 4