Skip to content

ADR 0008: Reusable workflows for credential isolation (undecided) - #107

Closed
ralphbean wants to merge 5 commits into
mainfrom
adr-0008-reusable-workflows
Closed

ADR 0008: Reusable workflows for credential isolation (undecided)#107
ralphbean wants to merge 5 commits into
mainfrom
adr-0008-reusable-workflows

Conversation

@ralphbean

Copy link
Copy Markdown
Member

Summary

  • Proposes reusable workflows so secrets (GitHub App private key) live only in .fullsend repo
  • Enrolled repos invoke the reusable workflow via workflow_call, never touching secrets directly
  • Status: Undecided — the premise that workflow_call provides credential isolation needs experimental validation
  • Experiment plan documented: test whether calling workflows can access called workflow secrets
  • Three options compared: reusable workflows, org-level secrets, external secret injection

Dependencies

🤖 Generated with Claude Code

ralphbean and others added 4 commits March 27, 2026 16:57
Establishes that control flows strictly downward through the execution
stack (Dispatch → Infrastructure → Sandbox → Harness → Runtime) and
no layer may influence layers above it. Distinguishes prohibited upward
control flow from permitted upward data flow (telemetry, failure signals).

Renames "Work Coordinator" to "Agent Dispatch and Coordination Layer"
for clarity. Adds an "Execution Stack" section to architecture.md.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Deterministic code paths (agent runtime wrapper and skill scripts) use
a shared library (forgekit) for forge-portable operations. Agents
themselves use native forge CLIs — LLMs adapt naturally to the forge
they're working with.

Updates architecture.md: adds Forge Abstraction Layer section, updates
Identity Provider for forgekit credential issuance, generalizes dispatch
layer to use forge-neutral language.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
GitHub Actions is the first execution platform, serving as both trigger
and infrastructure. A platform-agnostic entry point ensures nothing
below the infrastructure layer knows it's running on GH Actions.
Kubernetes is the anticipated second platform.

Updates architecture.md: resolves the initial platform question in Agent
Infrastructure, adds GH Actions trigger note to Agent Dispatch and
Coordination Layer.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Proposes using GitHub reusable workflows so secrets (GitHub App private
key) live only in the .fullsend repo, structurally inaccessible to
enrolled repos. Marked undecided pending an experiment to verify that
workflow_call actually provides this isolation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

Undecided — the core premise (that GitHub reusable workflows prevent the
calling repo from accessing the called workflow's secrets) needs experimental
validation before this can be accepted.

@ifireball ifireball Mar 29, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Perhaps we can focus on leveraging workflow authorization instead of protecting long-lived secrets. The per-workflow token that GitHub issues in GITHUB_TOKEN for example, contains the REF of the reusable workflow in its job_workflow_ref field.

We can use AgentGateway to inspect the token on the road to the LLM in order to avoid having to make IT setup the trust for us in IAM or the GCP/Vertex equivalent.

@gbenhaim

Copy link
Copy Markdown
Contributor

IMO the only valid solution is to use oidc federation. It's the santdard nowadays and very easy to configure. https://docs.github.com/en/actions/how-tos/secure-your-work/security-harden-deployments/oidc-in-google-cloud-platform

@ifireball

Copy link
Copy Markdown
Member

IMO the only valid solution is to use oidc federation. It's the santdard nowadays and very easy to configure. https://docs.github.com/en/actions/how-tos/secure-your-work/security-harden-deployments/oidc-in-google-cloud-platform

Defining this needs a bunch of permissions on the Google Cloud side that IT does not seem to grant us:

  • roles/iam.workloadIdentityPoolAdmin
  • roles/iam.serviceAccountAdmin
  • roles/resourcemanager.projectIamAdmin
  • roles/serviceusage.serviceUsageAdmin

We wither need to start negotiating with IT to get those, or consider Agent Gateway

@ralphbean

Copy link
Copy Markdown
Member Author

I think the OIDC federation vs long-lived secrets decision is orthogonal — or at least layered on top of this one.

The core thing I want to decide here is that we isolate the right to act as the agent in a "namespace" where the owners of the individual repos don't have the ability to execute arbitrary code or arbitrary workflows. Whether it's an OIDC JWT or a long-lived secret, the owners of individual repositories in the org can't access them directly. At best, they'd have to get the agent to exfiltrate them — but let's take that up in a separate decision around sandboxing the agent runtime.

I've updated the ADR to sharpen this framing: Option 3 (OIDC/external secrets) is now presented as complementary to the execution isolation decision, not as a peer alternative. It also notes that job_workflow_ref in GitHub's OIDC token could actually reinforce Option 1's isolation — so the two approaches compose well.

OIDC federation vs stored secrets is orthogonal — the core decision is
isolating the agent's execution namespace from enrolled repo owners.
Credential provisioning is now a separate discussion section rather than
a competing option.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

### Experiment needed

Create a test GitHub App with minimal permissions. Set up:

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.

Maybe I missed something, why do we need a github app? The GITHUB_TOKEN provided by default to workshops doesn't have enough permissions?

@gbenhaim gbenhaim left a comment

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.

I'm not sure about the central repo approach. It means that credentials are going to be shared by different users and it's difficult to see the benefits of it without more examples on which secrets are going to be stored.

@gbenhaim

Copy link
Copy Markdown
Contributor

I think the OIDC federation vs long-lived secrets decision is orthogonal — or at least layered on top of this one.

The core thing I want to decide here is that we isolate the right to act as the agent in a

Does it mean there is a single agent in fullsend everyone should use?

"namespace" where the owners of the individual repos don't have the ability to execute arbitrary code or arbitrary workflows. Whether it's an OIDC JWT or a long-lived secret, the owners of individual repositories in the org can't access them directly. At best, they'd have to get the agent to exfiltrate them — but let's take that up in a separate decision around sandboxing the agent runtime.

I've updated the ADR to sharpen this framing: Option 3 (OIDC/external secrets) is now presented as complementary to the execution isolation decision, not as a peer alternative. It also notes that job_workflow_ref in GitHub's OIDC token could actually reinforce Option 1's isolation — so the two approaches compose well.

@waynesun09

Copy link
Copy Markdown
Member

Question on the workflow_call shim pattern and credential isolation.

The shim workflow in enrolled repos passes the App private key to the reusable workflow:

jobs:
  agent:
    uses: {org}/.fullsend/.github/workflows/agent.yaml@main
    secrets:
      APP_PRIVATE_KEY: ${{ secrets.FULLSEND_APP_PRIVATE_KEY }}

For this to work, the enrolled repo must have access to FULLSEND_APP_PRIVATE_KEY (either as an org-level secret scoped to that repo, or copied to it). This means the private key is accessible to any workflow in the enrolled repo — a malicious PR adding a new workflow or modifying the shim could exfiltrate it.

workflow_call doesn't actually isolate credentials here — the caller must already have the secret in order to pass it. The reusable workflow runs on the calling repo's runner, not the .fullsend repo's runner, so there's no execution boundary that protects the key.

From the PR #119 experiment (nonflux/integration-service), the full workflow approach (complete agent workflows in the target repo, referencing org secrets directly) has the same credential exposure but avoids the cross-repo dependency. The tradeoff is update burden at scale vs. a single point of failure in .fullsend.

The only architecture that truly isolates the private key from enrolled repos would be a relay pattern — a lightweight service (Cloud Run, Lambda) that receives webhooks, holds the private key, mints a scoped installation token, and passes just the token to the enrolled repo's workflow via workflow_dispatch input. This breaks the "zero infrastructure" goal but is the only way the key never touches enrolled repos.

Is the credential isolation in this ADR's title referring to a different isolation boundary than key isolation from enrolled repos?

@ralphbean

Copy link
Copy Markdown
Member Author

Yeah, agreed - workflow_call is unacceptable; let's explore workflow_dispatch.

@ralphbean
ralphbean changed the base branch from adr-0007-github-actions-platform to main April 7, 2026 17:14
@ralphbean
ralphbean marked this pull request as ready for review April 7, 2026 17:14
@ralphbean ralphbean closed this Apr 7, 2026
@ralphbean

Copy link
Copy Markdown
Member Author

Dropping this. See #167

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.

4 participants