Guide

Delegating Resource Authority with Attenuation

Delegate Schema-owned Resource scopes through child births and reject any attempted widening.

Requirement closure answers which provider a child will use. It does not have to grant that child every key the provider could serve. Attenuation puts an authored authority boundary on the child edge: this descendant may use this Resource, within this durable scope, and no wider.

That distinction matters whenever one provider fronts many tenants, namespaces, repositories, accounts, or documents. A root may own acme; a reusable reporting child may need only acme/public. The child should receive the latter capability—not the former capability plus a request to “please be careful.”

Authority belongs to the Resource contract

Calling Resource.attenuate(...) adds three exact pieces to the Resource definition:

ScopeSchema.Struct({ prefix })

The durable value carried across child births.

Claim relationallows(scope, claim)

Whether one key and permit count are inside delegated authority.

Edge relationnarrows(parent, child)

Whether a deeper child scope is no broader than its parent.

The Resource owns these relations because every provider and consumer of that Resource must agree on what a scope means. A child edge supplies only a scope value. It cannot replace authorization policy with a local predicate.

const DocumentStore = Resource.make("DocumentStore", {
  key: Schema.String,
  resource: Schema.Struct({ contents: Schema.String }),
}).attenuate({
  scope: Schema.Struct({ prefix: Schema.String }),
  allows: ({ scope, claim }) => within(scope.prefix, claim.key),
  narrows: ({ parent, child }) => within(parent.prefix, child.prefix),
});

The scope Schema can cross a durable child boundary. allows and narrows remain executable policy owned by the registered Resource definition; their canonical references are part of the compiled chart contract.

Delegation is checked twice

The first check protects delegation itself. If a parent birth already carries acme, a child edge may request acme/public; it may not jump to other. Motive evaluates narrows before constructing the next immutable child birth.

The second check protects each use of the capability. Before acquisition, the provider evaluates allows with the effective scope and the exact claim { key, permits }. A claim outside the scope is a Resource attenuation defect; the provider does not acquire first and filter afterward.

Read those checks through one three-level chart:

Delegated authority

Authority narrows as it travels down.

Follow one exact DocumentStore capability from the root's acme scope into a leaf that may read only acme/public, then try to widen it.

1 · Definition-owned policy

The Resource defines what delegated authority means.

DocumentStore.attenuate gives one durable scope Schema two relations. allows decides whether a concrete key claim is inside a scope; narrows decides whether a child scope stays inside its parent's authority.

import { Activity, Resource, State, Statechart, States, Transitions } from "@motive/motive";
import { Schema } from "effect";

const within = (prefix: string, candidate: string): boolean =>
  candidate === prefix || candidate.startsWith(`${prefix}/`);

export const DocumentStore = Resource.make("DocumentStore", {
  key: Schema.String,
  resource: Schema.Struct({ contents: Schema.String }),
}).attenuate({
  scope: Schema.Struct({ prefix: Schema.String }),
  allows: ({ scope, claim }) => within(scope.prefix, claim.key),
  narrows: ({ parent, child }) => within(parent.prefix, child.prefix),
});

export class Begin extends Schema.TaggedClass<Begin>()("Begin", {}) {}

const ReadDocument = Activity.make("ReadDocument", {
  input: Schema.Struct({ document: DocumentStore.Input }),
  success: Schema.Void,
});

class Leaf extends State.Compound<Leaf>()("Leaf") {
  static states = States.make(() => [Idle, Reading]);
}

class Idle extends State.Atomic<Idle>()("Idle") {
  static transitions = Transitions.make(this, ({ on }) => [
    on(Begin, Reading),
  ]);
}

class Reading extends State.Atomic<Reading>()("Reading") {
  static transitions = Transitions.make(this, ({ invoke, on }) => [
    invoke(ReadDocument, () => ({
      document: DocumentStore.acquire("acme/public/handbook.md"),
    })),
    on([ReadDocument.Done, ReadDocument.Error]),
  ]);
}

export const LeafChart = Statechart.make(Leaf);

2 · Child-edge authority

A spawn edge states the authority it delegates.

Intermediate does not receive an ambient DocumentStore. Its child edge requests { prefix: "acme/public" } for the exact open DocumentStore.Input requirement carried by LeafChart.

import { Activity, Resource, State, Statechart, States, Transitions } from "@motive/motive";
import { Schema } from "effect";

const within = (prefix: string, candidate: string): boolean =>
  candidate === prefix || candidate.startsWith(`${prefix}/`);

export const DocumentStore = Resource.make("DocumentStore", {
  key: Schema.String,
  resource: Schema.Struct({ contents: Schema.String }),
}).attenuate({
  scope: Schema.Struct({ prefix: Schema.String }),
  allows: ({ scope, claim }) => within(scope.prefix, claim.key),
  narrows: ({ parent, child }) => within(parent.prefix, child.prefix),
});

export class Begin extends Schema.TaggedClass<Begin>()("Begin", {}) {}

const ReadDocument = Activity.make("ReadDocument", {
  input: Schema.Struct({ document: DocumentStore.Input }),
  success: Schema.Void,
});

class Leaf extends State.Compound<Leaf>()("Leaf") {
  static states = States.make(() => [LeafIdle, Reading]);
}

class LeafIdle extends State.Atomic<LeafIdle>()("LeafIdle") {
  static transitions = Transitions.make(this, ({ on }) => [
    on(Begin, Reading),
  ]);
}

class Reading extends State.Atomic<Reading>()("Reading") {
  static transitions = Transitions.make(this, ({ invoke, on }) => [
    invoke(ReadDocument, () => ({
      document: DocumentStore.acquire("acme/public/handbook.md"),
    })),
    on([ReadDocument.Done, ReadDocument.Error]),
  ]);
}

export const LeafChart = Statechart.make(Leaf);

class Intermediate extends State.Compound<Intermediate>()("Intermediate") {
  static states = States.make(() => [Idle, Delegating]);
}

class Idle extends State.Atomic<Idle>()("Idle") {
  static transitions = Transitions.make(this, ({ on }) => [
    on(Begin, Delegating),
  ]);
}

class Delegating extends State.Atomic<Delegating>()("Delegating") {
  static transitions = Transitions.make(this, ({ spawn }) => [
    spawn(LeafChart).attenuate(DocumentStore, { prefix: "acme/public" }),
  ]);
}

export const IntermediateChart = Statechart.make(Intermediate);

3 · Monotone delegation

Every deeper scope must be no broader than its parent.

Application closes the requirement and delegates acme. Intermediate may narrow that authority to acme/public; a sibling prefix such as other is rejected before the leaf's immutable Birth can be created.

import { Activity, Resource, State, Statechart, States, Transitions } from "@motive/motive";
import { Schema } from "effect";

const within = (prefix: string, candidate: string): boolean =>
  candidate === prefix || candidate.startsWith(`${prefix}/`);

export const DocumentStore = Resource.make("DocumentStore", {
  key: Schema.String,
  resource: Schema.Struct({ contents: Schema.String }),
}).attenuate({
  scope: Schema.Struct({ prefix: Schema.String }),
  allows: ({ scope, claim }) => within(scope.prefix, claim.key),
  narrows: ({ parent, child }) => within(parent.prefix, child.prefix),
});

export class Begin extends Schema.TaggedClass<Begin>()("Begin", {}) {}
const DocumentStores = DocumentStore.as("DocumentStores");

class Application extends State.Compound<Application>()("Application") {
  static states = States.make(() => [Idle, Running]);
}

class Idle extends State.Atomic<Idle>()("Idle") {
  static transitions = Transitions.make(this, ({ on }) => [
    on(Begin, Running),
  ]);
}

class Running extends State.Atomic<Running>()("Running") {
  static transitions = Transitions.make(this, ({ provide, spawn }) => [
    provide(DocumentStores),
    spawn(IntermediateChart).attenuate(DocumentStore, { prefix: "acme" }),
  ]);
}

const ReadDocument = Activity.make("ReadDocument", {
  input: Schema.Struct({ document: DocumentStore.Input }),
  success: Schema.Void,
});

class Leaf extends State.Compound<Leaf>()("Leaf") {
  static states = States.make(() => [LeafIdle, Reading]);
}

class LeafIdle extends State.Atomic<LeafIdle>()("LeafIdle") {
  static transitions = Transitions.make(this, ({ on }) => [
    on(Begin, Reading),
  ]);
}

class Reading extends State.Atomic<Reading>()("Reading") {
  static transitions = Transitions.make(this, ({ invoke, on }) => [
    invoke(ReadDocument, () => ({
      document: DocumentStore.acquire("acme/public/handbook.md"),
    })),
    on([ReadDocument.Done, ReadDocument.Error]),
  ]);
}

export const LeafChart = Statechart.make(Leaf);

class Intermediate extends State.Compound<Intermediate>()("Intermediate") {
  static states = States.make(() => [IntermediateIdle, Delegating]);
}

class IntermediateIdle extends State.Atomic<IntermediateIdle>()("IntermediateIdle") {
  static transitions = Transitions.make(this, ({ on }) => [
    on(Begin, Delegating),
  ]);
}

class Delegating extends State.Atomic<Delegating>()("Delegating") {
  static transitions = Transitions.make(this, ({ spawn }) => [
    spawn(LeafChart).attenuate(DocumentStore, { prefix: "acme/public" }),
  ]);
}

export const IntermediateChart = Statechart.make(Intermediate);

export const RootChart = Statechart.make(Application);

The first edge establishes authority

The root provider itself is not born with a delegated scope. The first child edge establishes one:

spawn(IntermediateChart).attenuate(DocumentStore, { prefix: "acme" });

That scope is attached to the exact DocumentStore.Input delegation produced by requirement closure. It does not authorize unrelated Resources, and an attenuation target that does not match one of the child's Resource requirements is rejected during chart assembly.

The scope then becomes part of the child's immutable birth record beside the pinned provider occurrence. Recovery does not rediscover or recompute which tenant the child was allowed to use.

Deeper edges may only narrow

IntermediateChart can delegate the subset its leaf needs:

spawn(LeafChart).attenuate(DocumentStore, { prefix: "acme/public" });

Because acme/public is within acme, the Resource's narrows relation accepts the edge. The leaf birth carries only the narrower scope. If the intermediate instead requests { prefix: "other" }, child construction defects with ResourceAttenuationNarrowingFailed; no wider birth is published.

This is a monotone authority law, not a naming convention. Prefixes are only this example's scope model. A Resource may use account sets, path capabilities, regions, roles, or another Schema-owned scope as long as allows and narrows define the two relations precisely.

Claims are authorized at the provider

The leaf's authored claim remains an ordinary durable Resource claim:

DocumentStore.acquire("acme/public/handbook.md");

At runtime the engine proves that the claim belongs to the child occurrence, points at the pinned provider, and matches the expected grant. It then decodes the delegated scope and runs allows before provider acquisition. The process-local document contents never become the authorization token; the durable key and scope are enough to reproduce the decision.

Attenuation therefore is not data filtering. A child never receives all documents and trims the result to a public subset. It receives authority to make only claims admitted by its scope.

Output authority needs a pre-publication claim

Resource.Output normally creates its durable key after an Activity returns process-local material. That is too late to authorize the key at child birth. Motive therefore refuses output attenuation with ResourceOutputAttenuationRequiresPrePublicationClaim until the model can name a key that the scope may authorize before delegating publication authority.

This restriction keeps the same rule in both directions: delegated authority must be checkable before the external operation it controls.

Test the authority boundary

  1. Inspect the Resource definition and verify the scope Schema plus both policy relations survive registration.
  2. Inspect the root child mount and verify it carries { prefix: "acme" } for the exact DocumentStore.Input requirement.
  3. Construct the intermediate birth and verify its immutable delegation retains that scope.
  4. Construct the leaf birth and verify the scope narrows to { prefix: "acme/public" }.
  5. Replace the leaf scope with { prefix: "other" } and verify birth fails with ResourceAttenuationNarrowingFailed.
  6. Claim acme/private/payroll.csv from the public leaf and verify provider acquisition never begins.
  7. Remove the Resource attenuation contract, target an unrelated requirement, or declare the same target twice and verify chart assembly refuses each invalid boundary.

Next, Contention and Weighted Capacity keeps the same provider authority and asks a different question: when several admitted claims compete, who may enter now?