Skip to content

File options

Set at the top level of a .proto file:

option (spec.service) = {name: "notes"};

spec.string_const

Set on file · Type repeated StringConst · Number 50001

StringConst defines string constants at the file level

spec.int_const

Set on file · Type repeated IntConst · Number 50002

IntConst defines integer constants at the file level

spec.float_const

Set on file · Type repeated FloatConst · Number 50003

FloatConst defines floating-point constants at the file level

spec.bool_const

Set on file · Type repeated BoolConst · Number 50004

BoolConst defines boolean constants at the file level

spec.generate_user_api

Set on file · Type bool · Number 50010

GenerateUserApi marks this proto file for user API code generation
When true, tacplus will generate service wrappers, HTTP clients, and example implementations

spec.service

Set on file · Type ServiceAnnotation · Number 50011

service declares the owning service identity for this proto file.
This is the source of truth for the generated service schema injected into sys/tacplus.proto.

spec.state_imports

Set on file · Type repeated StateImport · Number 50012

state_imports is a legacy file-level mirror for field-level state_import roots.
Prefer field-level state_import; if this option is present, it must exactly
mirror the imported "package.StateName" roots derived from those fields.

spec.opa_import

Set on file · Type repeated OpaImport · Number 50013

opa_import declares dependencies on OPA eval definitions.
Each entry references a "package.InputMessageName" that must have spec.opa_eval.
tacplus generates typed client methods on the importing service's SM.

spec.timeseries_store

Set on file · Type TimeseriesStoreAnnotation · Number 50015

timeseries_store marks this service as a timeseries data store. The store
receives its projection data by subscribing to the EventBus topics that
carry its projection sources (no mutation service is generated).

Example: option (spec.timeseries_store) = {};

\50016, 50017 retired (graph_import/timeseries_import — push-mutation imports removed).

spec.grpc_s2s_client

Set on file · Type repeated GrpcS2SClient · Number 50018

grpc_s2s_client declares service-to-service gRPC client dependencies.
tacplus generates typed gRPC client wrappers for each referenced service.

Example: option (spec.grpc_s2s_client) = {service: "identity.Mint"};

spec.grpc_c2s_client

Set on file · Type repeated GrpcC2SClient · Number 50019

grpc_c2s_client declares client-facing service dependencies for this service.
tacplus.yaml selects whether those dependencies emit C2S, W2S, or both.

Example: option (spec.grpc_c2s_client) = {service: "gateway.TokenMint"};

spec.grpc_w2s_client

Set on file · Type repeated GrpcW2SClient · Number 50020

grpc_w2s_client declares browser/web client-facing service dependencies.
tacplus.yaml selects whether those dependencies emit C2S, W2S, or both.

Example: option (spec.grpc_w2s_client) = {service: "dashboard.Dashboard"};

spec.all_grpc_s2s_clients

Set on file · Type bool · Number 50021

all_grpc_s2s_clients declares that this service/client needs gRPC client
wrappers for ALL gRPC services in the proto source. Intended for tooling
(e.g. a CLI) that must be able to call every service.

Example: option (spec.all_grpc_s2s_clients) = true;

spec.vservice

Set on file · Type VServiceAnnotation · Number 50022

vservice declares a virtual service identity for this proto file.
Virtual services are not physical processes — their RPCs are hosted by a
physical service (identified by the service_event label). vservice identity
enables entitlement-based C2S authorization: aud targets the physical host
while entitlements are checked against the vservice name.

Example: option (spec.vservice) = {id: 30, name: "telemetry"};

spec.record_store

Set on file · Type RecordStoreAnnotation · Number 50023

record_store marks this service as the full-record store. Tiering
(hot/cold) is internal to the service. tacplus auto-derives EventBus
subscriptions for the record types it stores (currently spec.record).

Example: option (spec.record_store) = {};

\50024 retired (cold_store — tiering is internal to the store service).

spec.trait

Set on file · Type TraitAnnotation · Number 50025

trait declares a cross-service trait implemented by a host service.
Like vservice, a trait is not a physical process: its RPCs are hosted by a
physical service while entitlements are checked against the trait name.
A trait declaration populates the same identity the vservice option does.

Example: option (spec.trait) = {id: 40, name: "query"};

spec.record_read

Set on file · Type repeated RecordRead · Number 50026

record_read declares that THIS service reads the stored rows of the named
(spec.record) types, through the record query plane's S2S half.

It is the USE half of a paired control whose GRANT half is
(spec.record).s2s_read on the record itself. One says "these services may
read my rows"; this says "I read that record's rows". The generator refuses
a build in which one is declared without the other, so a client that would
be denied at runtime cannot be generated, and a grant nothing uses cannot
sit unnoticed.

Declaring it is the ONLY thing the reader has to write. It implies the
underlying (spec.grpc_s2s_client) on the trait's S2S half — the reader does
not restate the transport dependency, because the transport is a
consequence of the read, not a second decision.

What it generates is a typed, per-record reader on the service manager that
collapses the plane's three-call query lifecycle
(Create<T>Query -> List<T> -> Delete<T>Query) into the two questions a
reader actually asks:

get(key)              -> Option<T>       point read by the record's key
list(filter, page)    -> (Vec<T>, token) paged read over a typed predicate

Adding the c2s block asks for a SECOND surface on top of that reader: a
synthesized <Service>Records sibling service in THIS service's own proto,
carrying Get<T> and Get<T>Page — a client-facing read of the same rows,
scoped to the calling person and their tenant by pins the generated handler
applies before the store is asked. Its grant half is
(spec.record).c2s_read, and it is a separate right from s2s_read: reading a
record type with your own authority and DISCLOSING it to a human are
different decisions, and the record owner makes the second one.

Example: option (spec.record_read) = {record: "messaging.Message"};

Example, with the client-facing half:

option (spec.record_read) = {
  record: "assistant.AssistantActivity"
  c2s: {scope: C2S_SCOPE_PRINCIPAL, exposure: RPC_EXPOSURE_EXTERNAL}
};