NATS Workloads and Grants
The wasmcloud-nats host plugin lets a WebAssembly component publish, subscribe, consume JetStream, and read and write KV buckets through the wasmcloud:nats@0.1.0 interface. The component never opens a connection or holds a credential: the host owns the NATS client, and the component gets exactly the subjects, streams and buckets an operator granted it.
This page covers where configuration is set, the three grants and what each one actually gates, and the refusals you will meet if a grant and a subscription disagree.
For sizing (e.g., subscription capacity, acknowledgement windows, instance pools) see NATS Tuning.
Configuration
There are three configuration surfaces:
Connection and identity belong to the operator. servers, jetstream-domain, inbox-prefix, and every credential: creds, jwt, nkey-seed, username, password, token, tls-ca, tls-cert, tls-key. These live on the hostgroup's wasmcloud-nats plugin entry. A workload manifest that sets one is refused, so a component cannot redirect itself to another server or substitute another credential.
Grants are operator-declared ceilings. subject-allow, stream-allow, bucket-allow. Deny by default.
Behaviour belongs to the workload, within the grant: core-subscriptions, jetstream-subscriptions, kv-watches, ack-mode, max-in-flight, max-ack-pending, max-deliver, subscription-capacity, subscription-capacity-bytes, request-timeout-ms.
The operator declares the ceiling in the hostgroup's values:
hostPlugins:
- id: wasmcloud-nats
config:
servers: nats://nats.nats.svc.cluster.local:4222
subject-allow: demo.>
stream-allow: DEMO
bucket-allow: demoand a workload asks for a subset of it:
hostInterfaces:
- namespace: wasmcloud
package: nats
version: "0.1.0"
interfaces: [types, jetstream, core-handler]
config:
subject-allow: demo.events,done.demo.>
core-subscriptions: demo.events
max-in-flight: "8"The three grants
subject-allow covers:
- publish and request
- core subscriptions
- the filter on a JetStream subscription or pull consumer
- the subjects a stream stores, when you read stored messages
stream-allow names the streams that may be read.
bucket-allow names the KV buckets.
This split has two important consequences:
- Publishing to a subject does not grant reading the stream that captures it. They are different grants.
stream-allowon its own reaches nothing readable. Stored messages are checked againstsubject-allowtoo, so a stream grant without the subjects the stream stores returns empty rather than erroring. Grant the subjects alongside the stream.
Grants are compared differently
subject-allow entries are NATS subject patterns, so a ceiling of demo.> covers demo.orders.
stream-allow and bucket-allow are plain names compared as an exact subset. A ceiling of * does not cover a stream called LOAD. Name the streams and buckets literally.
Narrowing, under workloadConfig: deny
Cosmonic Control runs the plugin with workloadConfig: deny. Under deny a workload may ask for a subset of the operator's ceiling. There are three outcomes, not two:
| The workload manifest | The operator's ceiling | Result |
|---|---|---|
| narrows a grant | declared | accepted |
| asks for more than the ceiling | declared | refused |
| sets a grant | never declared for that key | refused |
A workload that sets stream-allow on a host whose operator declared only subject-allow is refused, with a message about a ceiling that does not exist. Ask the operator to declare the ceiling you need; deleting the line instead is not a fix, because it leaves the workload running at whatever the operator does allow.
Keep the grants in your manifest. deny refuses widening, not carrying.
Pull consumers need an explicit filter
A pull consumer carries its own filter subject, and the host checks that filter against subject-allow when the component attaches.
nats consumer add with no --filter creates a consumer whose filter is >, which no narrower grant contains. The fetch is then denied before it runs:
Denied(Denial { reason: NotGranted, target: Subject, name: ">" })
Create pull consumers with the narrowest filter that covers what they read, and grant exactly that:
nats consumer add DEMO workers --pull --filter 'demo.stream.>' --defaultsReserved space
Some subjects are denied regardless of grant: $JS., $SYS., $KV., $OBJ., the plugin's own delivery plane, and the lattice control subjects runtime.host.* and runtime.operator.*.
Lifecycle is out of band
The plugin creates nothing. Streams, consumers and buckets are provisioned with the nats CLI or your deployment tooling before a workload reaches for them. A component that opens a bucket which does not exist gets not-found, not a new bucket.
Which NATS interface should I use?
wasmcloud:nats is NATS-specific and exposes what NATS has: JetStream, KV, pull consumers, and the grant model above.
wasmcloud:messaging is the generic messaging abstraction: publish, subscribe, request, portable across brokers. Neither supersedes the other. Choose the generic interface when portability matters more than reach, and the NATS one when you need JetStream, KV, or the grants.
Templates
Cosmonic provides component templates demonstrating seven patterns (in Rust and Go) that you can build on:
| Template | Shape | Delivery guarantee | Use when… |
|---|---|---|---|
| nats-core-subscriber | Host subscribes, calls the component once per message | none, fire and forget | Telemetry, cache invalidation, notifications. The cheapest consumer there is |
| nats-jetstream-consumer | Host pushes stream messages, paced by acknowledgement | at-least-once, with redelivery | Delivery has to be guaranteed, and you want backpressure rather than drops |
| nats-request-reply | Host subscribes, the component publishes one reply to the request's inbox | none; the requester's timeout is the only bound | A caller needs an answer over NATS: lookup, validation, RPC on subjects |
| nats-fan-out | One event in, a fixed set of downstream subjects out | none, and partial failure is not repairable | One event needs reshaping onto several subjects, routed in one place |
| nats-jetstream-worker | Guest opens a pull consumer and fetches batches at its own pace | at-least-once; you acknowledge each message | The guest must control the rate: scheduled drains, a rate-limited downstream |
| nats-kv-store | Messages on a subject become keys in a JetStream KV bucket | durable write, revisioned | Device state, per-tenant settings, a last-known-value cache |
| nats-kv-watcher | Host watches a bucket, calls the component per change | replayed; a redelivery can repeat one | Reload config, invalidate a cache, mirror state onward |
See NATS templates for more information.