Skip to content

feat: 30 workflows to wire relayauth/relayfile permissions into workflow runner#673

Merged
khaliqgant merged 3 commits into
mainfrom
worktree-relayauth-workflows
Apr 7, 2026
Merged

feat: 30 workflows to wire relayauth/relayfile permissions into workflow runner#673
khaliqgant merged 3 commits into
mainfrom
worktree-relayauth-workflows

Conversation

@khaliqgant
Copy link
Copy Markdown
Member

@khaliqgant khaliqgant commented Mar 31, 2026

Summary

  • 30 TypeScript workflows organized into 9 dependency-ordered waves
  • Wires up relayauth/relayfile permissions into the workflow runner so agents spawned via agent-relay run get proper tokens, scopes, and filesystem enforcement
  • Master executor script (run-waves.sh) runs all waves with parallelism within each wave
  • Exercises 9 different swarm patterns: pipeline, dag, fan-out, debate, hierarchical, consensus, mesh, hub-spoke, supervisor

Architecture (6 layers)

  1. Types & SchemaAgentPermissions interface, permissions field on AgentDefinition
  2. Shared Provisioner — Extract provisioning from relay on into reusable packages/sdk/src/provisioner/
  3. SDK + BrokeragentToken field on SpawnPtyInput, broker /api/spawn accepts token
  4. Workflow Runner — Provisioning lifecycle: compile scopes → mint tokens → seed ACLs → pass at spawn → cleanup
  5. Enforcement — Relayfile-mount activated per-agent with scoped tokens
  6. UX — YAML permissions DSL, dry-run output, preflight validation, audit log, docs

Wave Breakdown

Wave Workflows Pattern(s) What
1 01-03 pipeline Foundation types, SpawnInput, schema
2 04-06 dag, debate Shared provisioner, token factory, merge resolver
3 07-10 dag, hierarchical SDK client, Rust broker, runner lifecycle
4 11-14 dag, fan-out Mount, env chain, preset permissions, ACL seeding
5 15-18 debate, pipeline YAML DSL, dry-run, validation, audit log
6 19-22 fan-out, hub-spoke Unit tests, integration tests, e2e
7 23-25 fan-out, pipeline Docs (MDX+MD), builtin templates, PR 663 readability
8 26-28 consensus, mesh, debate Security audit, cross-track review, reflection
9 29-30 supervisor, pipeline Final validation, release prep

Key Design Decisions

  • Debate pattern (06, 15): Two agents argue competing designs (YAML-first vs additive merge; minimal vs expressive DSL), a judge picks the winner
  • Consensus pattern (26): 3 independent security auditors must agree before shipping
  • Hierarchical pattern (09): Lead coordinates 3 workers for the core runner integration
  • Mesh pattern (27): 4 domain experts freely collaborate on cross-track issues
  • Reflection (28): Optimist vs critic debate with judge synthesizing a retrospective

Running

# Run all 30 workflows in order
./workflows/relayauth-integration/run-waves.sh

# Run a single workflow
agent-relay run workflows/relayauth-integration/01-permission-types.ts

# Run a wave manually (parallel)
agent-relay run workflows/relayauth-integration/01-permission-types.ts &
agent-relay run workflows/relayauth-integration/02-spawn-input-token.ts &
agent-relay run workflows/relayauth-integration/03-schema-permissions.ts &
wait

What exists today

  • relay on already does full provisioning (dotfiles → scopes → JWT → ACL seeding)
  • The Rust broker already supports RELAY_AGENT_TOKEN env var propagation via spawn_wrap_with_token()
  • @relayauth/core and @relayfile/sdk are installed as dependencies

What these workflows build

  • The bridge: workflow runner calls the same provisioning pipeline that relay on uses
  • The plumbing: SpawnPtyInput.agentToken → SDK client → broker API → RELAY_AGENT_TOKEN env
  • The enforcement: agents get scoped tokens, relayfile-mount enforces ACLs
  • The UX: permissions: in relay.yaml, presets from roles, dry-run shows access

Test plan

  • Dry-run each workflow: agent-relay run --dry-run <workflow>
  • Run Wave 1 (foundation types) end-to-end
  • Verify backwards compatibility: existing workflows without permissions still work
  • Security audit workflow (26) produces consolidated report
  • Final validation workflow (29) passes all checks

🤖 Generated with Claude Code


Open with Devin

devin-ai-integration[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

github-advanced-security[bot]

This comment was marked as resolved.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 6, 2026

Preview deployed!

Environment URL
Web https://pr-673.agentrelay.net

This preview will be cleaned up when the PR is merged or closed.

devin-ai-integration[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

Wire per-agent permissions through the workflow runner lifecycle:
- AgentPermissions type with access presets, file globs, network, exec
- Permission profiles for reusable named configurations
- Provisioner module: compiler, token factory, seeder, mount, audit
- Dry-run permission resolution and display
- Network permission supports boolean and object {allow, deny} forms
- 6 example workflows demonstrating permission patterns
- Comprehensive docs (MDX + MD mirror)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@khaliqgant khaliqgant force-pushed the worktree-relayauth-workflows branch from 333a746 to 960a619 Compare April 7, 2026 10:30
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 2 new potential issues.

View 31 additional findings in Devin Review.

Open in Devin Review

Comment thread src/main.rs
Comment on lines 984 to +988
}
};

// Caller-supplied agent_token overrides auto-registration
let worker_relay_key = agent_token.or(worker_relay_key);
Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration Bot Apr 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Fatal Relaycast registration aborts spawn before caller-supplied agent_token can be used

When the broker receives a spawn request with an agent_token, it still unconditionally calls retry_agent_registration at src/main.rs:966. If this returns RegRetryOutcome::Fatal (e.g. the Relaycast SDK client is not initialized, which is common when no RELAY_API_KEY is set), the handler executes continue at line 982, aborting the spawn before the agent_token.or(worker_relay_key) override at line 988 is reached. This means a perfectly valid caller-supplied agent_token is silently ignored, and the spawn fails with a pre-registration error. The SDK's WorkflowRunner now sends agentToken on every spawn when permissions are configured (runner.ts:5753), so any environment without a Relaycast API key will have all permission-enabled workflow spawns fail.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment thread packages/sdk/src/provisioner/types.ts Outdated
Comment on lines +33 to +34
/** JWT token TTL in seconds. Default: 3600 (1 hour). */
tokenTtlSeconds?: number;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 WorkflowProvisionConfig.tokenTtlSeconds documents default as 3600 but actual default is 7200

The JSDoc comment on tokenTtlSeconds in packages/sdk/src/provisioner/types.ts:33 states Default: 3600 (1 hour), but when the field is omitted, mintAgentToken in packages/sdk/src/provisioner/token.ts:3 uses DEFAULT_WORKFLOW_TOKEN_TTL_SECONDS = 2 * 60 * 60 (7200 seconds / 2 hours). External consumers of the provisionWorkflowAgents() API who rely on the documented default will unknowingly mint tokens that live twice as long as expected. The WorkflowRunner is unaffected because it explicitly passes tokenTtlSeconds: 3600 at runner.ts:1557.

Suggested change
/** JWT token TTL in seconds. Default: 3600 (1 hour). */
tokenTtlSeconds?: number;
/** JWT token TTL in seconds. Default: 7200 (2 hours). */
tokenTtlSeconds?: number;
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 1 new potential issue.

View 27 additional findings in Devin Review.

Open in Devin Review

errors.push(`${label}.scopes must not contain empty strings`);
continue;
}
if (!/^[^:\s]+:[^:\s]+:[^:\s]+:.+$/u.test(scope)) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Scope validation regex rejects valid 2-3 segment custom scopes, blocking workflow execution

The scope validation regex at packages/sdk/src/workflows/runner.ts:715 requires exactly 4+ colon-separated segments (/^[^:\s]+:[^:\s]+:[^:\s]+:.+$/u), but the codebase, documentation, and test fixtures all use scopes with fewer segments like relay:custom:deploy (3 segments) and relay:custom (2 segments). These are accepted by the compiler (packages/sdk/src/provisioner/compiler.ts) and baked into JWT tokens without issue, but when a user defines such scopes in their relay.yaml agent permissions, execute() throws Permission validation failed and blocks the entire workflow. The dryRun() path accumulates these as validation errors, marking the report as valid: false.

Affected scopes across the codebase

Compiler test (packages/sdk/src/provisioner/__tests__/compiler.test.ts:259): relay:custom:deploy (3 segments → FAIL)

Workflow runner test (packages/sdk/src/__tests__/workflow-runner.test.ts:1156): relay:custom (2 segments → FAIL)

Documentation example (docs/permissions.md): relay:custom:deploy (3 segments → FAIL)

All of these are passed through to the token factory and work fine at runtime — the validation is the sole gate rejecting them.

Prompt for agents
The scope validation regex at runner.ts:715 is too strict. It requires 4+ colon-separated segments (plane:resource:action:path) but valid scopes across the codebase and documentation use 2 or 3 segments (e.g. relay:custom, relay:custom:deploy, fs:read).

The regex /^[^:\s]+:[^:\s]+:[^:\s]+:.+$/u should be relaxed to accept any scope with at least 2 colon-separated non-empty, non-whitespace segments. For example: /^[^:\s]+:[^:\s]+(:[^:\s]+)*$/u would accept relay:custom, relay:custom:deploy, and relayfile:fs:read:/src/index.ts.

Alternatively, you could simply require a non-empty string with at least one colon and no whitespace: /^[^\s:]+(?::[^\s:]+)+$/u

After fixing, verify the test at workflow-runner.test.ts:1156 (scopes: ['relay:custom']) and the compiler test at compiler.test.ts:259 (scopes: ['relay:custom:deploy']) pass validation correctly.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

@khaliqgant khaliqgant merged commit bde8c14 into main Apr 7, 2026
41 of 42 checks passed
@khaliqgant khaliqgant deleted the worktree-relayauth-workflows branch April 7, 2026 11:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants