File options¶
Set at the top level of a .proto file:
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: