Skip to main content

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

and 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-allow on its own reaches nothing readable. Stored messages are checked against subject-allow too, 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 manifestThe operator's ceilingResult
narrows a grantdeclaredaccepted
asks for more than the ceilingdeclaredrefused
sets a grantnever declared for that keyrefused

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.>' --defaults

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

TemplateShapeDelivery guaranteeUse when…
nats-core-subscriberHost subscribes, calls the component once per messagenone, fire and forgetTelemetry, cache invalidation, notifications. The cheapest consumer there is
nats-jetstream-consumerHost pushes stream messages, paced by acknowledgementat-least-once, with redeliveryDelivery has to be guaranteed, and you want backpressure rather than drops
nats-request-replyHost subscribes, the component publishes one reply to the request's inboxnone; the requester's timeout is the only boundA caller needs an answer over NATS: lookup, validation, RPC on subjects
nats-fan-outOne event in, a fixed set of downstream subjects outnone, and partial failure is not repairableOne event needs reshaping onto several subjects, routed in one place
nats-jetstream-workerGuest opens a pull consumer and fetches batches at its own paceat-least-once; you acknowledge each messageThe guest must control the rate: scheduled drains, a rate-limited downstream
nats-kv-storeMessages on a subject become keys in a JetStream KV bucketdurable write, revisionedDevice state, per-tenant settings, a last-known-value cache
nats-kv-watcherHost watches a bucket, calls the component per changereplayed; a redelivery can repeat oneReload config, invalidate a cache, mirror state onward

See NATS templates for more information.