Services can declare cloud-managed resources in their contract. Accepting a deployment authority plan updates desired authority to include the resource boundary; authority reconciliation then provisions or updates the materialized resource and binding.
KV buckets
NATS KV buckets provide persistent state:
resources: {
kv: {
items: {
purpose: "Store item records",
schema: ref.schema("Item"),
history: 1,
ttlMs: 0,
},
},
}, The key (items) is a logical alias. During authority reconciliation, Trellis provisions the resource before making the accepted boundary runtime-available and persists the deployment binding. Physical bucket names remain scoped to the deployment/profile/lineage rather than to a single digest, so compatible rollouts can reuse the same underlying resource. TrellisService.connect(...) consumes those persisted bindings, resolves required bindings plus any optional bindings that were successfully provisioned, and exposes them as service.kv.items, which is typed and ready to use directly without an extra open step.
Resolved binding payloads are Trellis runtime internals. Service authors should
not call Trellis.Bindings.Get, construct TrellisService or resource handles,
or pass binding/resource data into Trellis constructors; connect with TrellisService.connect(...) and use the returned resource handles.
Store resources
Service-owned stores hold larger opaque blobs such as uploaded files, generated exports, or intermediate workflow artifacts.
resources: {
store: {
uploads: {
purpose: "Temporary uploaded files awaiting processing",
ttlMs: 86_400_000,
maxTotalBytes: 10 * 1024 * 1024 * 1024,
},
},
}, Like KV, the key (uploads) is a logical alias and Trellis assigns the physical store identity during authority reconciliation after the desired authority change is accepted. The intended service runtime shape mirrors KV closely: service.store.uploads.open() resolves a higher-level store object from the persisted binding, and its failable methods return Result values. Store bindings expose effective installed runtime limits; omitting maxTotalBytes means no finite total-size request and reconciles the object store back to the runtime’s unlimited/default setting. v1 does not advertise per-object limits until the runtime write path enforces them.
These examples use the default required: true. Optional KV and store resources may not appear in runtime bindings if provisioning is unavailable or fails.
Event consumer bindings
Durable service event processing is also binding-backed, but it is declared with top-level eventConsumers rather than under resources. Each group references events already subscribed through uses.events.subscribe. Authority acceptance mutates desired state; reconciliation provisions the physical JetStream consumer, persists the binding, and grants the service token only the exact consumer info, pull-next, and ack subjects for that binding.
Use eventConsumers when a service needs replay, redelivery, or an independent cursor. Use an ephemeral event listener for live-only reactions that do not need a durable cursor.
Public app state
Not all state belongs in service-owned resources. Trellis also ships a public trellis.state@v1 contract for semi-durable app memory that should be available across app instances, CLIs, and devices.
- apps and devices call
State.*over authenticated RPCs - namespaces are derived from the authenticated user or device, contract id lineage, and named store
- values are JSON and keys are opaque strings
- entries carry state-version provenance so apps and devices can migrate older stored values client-side
- services should still use
resources.kvdirectly for private service state or projections
State ownership is derived by Trellis; normal callers do not pass a public scope, OAuth scope, or capability to pick a state partition.
Use State for things like saved filters, drafts, UI preferences, and app memory that should follow the same user or device across machines. Use resources.kv when the state is owned by a service implementation instead of by an app or device participant.
Runtime streams
Services do not declare arbitrary stream resources in v1 contracts. Trellis-owned features such as jobs, operations, events, and transfers still use JetStream internally, but those streams are derived from first-class contract surfaces and installed bindings rather than from resources.streams entries.