Skip to content

Keep a durable record of who and why for Junior actions #2211

Open

Description

@sentry-junior

Junior cannot answer "who caused this action, and why?" after the fact without reading transcripts. Transcripts are not enough. They expire, they are prose, and they do not link a side effect to the authority that allowed it. Automations make this harder, because the person who is accountable, the person who last edited the instruction, and the event that triggered the run can all be different.

Current state

  • Junior binds credential authority correctly before it acts. packages/junior/src/chat/credentials/context.ts has signed subject bindings for Slack DMs, scheduled automations, event automations, and timer Watches. Junior does not store these decisions as records.
  • Conversation content is purged after 14 days (private) or 90 days (public) (packages/junior/src/chat/conversations/retention.ts).
  • Credentialed sandbox egress logs sandbox.egress.upstream.requested only outside production (packages/junior/src/chat/egress/credentialed.ts, shouldLogSandboxEgressInfo).
  • junior_automation_runs does not store which Automation version ran.
  • Bot JWTs set sub to the plugin name and carry no requester (packages/junior/src/chat/mcp/jwt-bearer-provider.ts).
  • Side effects outside Junior's own GitHub tools, such as workflow dispatches, have no attribution (Record who Junior acts for on workflow dispatches #2210).

Prior art

  • GitHub Copilot audit events carry actor_is_agent, agent_session_id, and user (the initiator), and GitHub keeps them for 180 days separately from session logs. GitHub attributes automation work to the Automation creator, who then cannot approve the PR. (audit events, risks and mitigations)
  • In Linear, an agent is an app identity that a human delegates work to. The human stays the owner. (Linear agents)
  • Microsoft Entra audit events mark both initiatedBy and performedBy with agentType. (Entra Agent ID logs)
  • RFC 8693 act claims and the OAuth Transaction Tokens draft (txn, purp, tctx) carry the actor and the purpose through a call chain. (draft)

Proposed approach (from David Cramer)

This is the broad direction, not a final design.

  • Write an append-only record for each external write and each sensitive credentialed read. Keep it separate from transcripts, with its own retention. Store only metadata, not request or response bodies.
  • Who has three fields: the accountable person (the requester, or the Automation creator), the actor that did the action, and the credential subject whose authority Junior used.
  • Why is a set of links, not prose: the Source, the Automation id and version, the Automation run, the trigger Event, and the Actors that steered the Turn. A purpose string from the model can be stored, but it is labeled untrusted and never grants authority.
  • Write the record where Junior uses credentials: credentialed egress, and host tools that cause side effects.
  • Put the same identifiers on outbound requests so other systems' logs match ours. This covers bot JWT actor claims, GitHub commit and workflow dispatch attribution, and a request id header for services we own.
  • Show the records in the dashboard, and export them for security review.

First steps:

  • Store the Automation version on each Automation run.
  • Keep a metadata-only credentialed egress log in production.

Related: #773, #2210, #584, #781.

via David Cramer.

--

View Junior Session [Sentry]

Activity

  1. sentry-junior commented on Oct 23, 2026

    @sentry-junior
    ContributorAuthor

    Finding while starting the second first step: enabling sandbox.egress.upstream.requested in production does not make a durable log by itself. In production, logging.ts (shouldSuppressInfoLog) drops all info-level logs before they reach Sentry. They only reach the console (Vercel runtime logs, short retention). So the shouldLogSandboxEgressInfo gate in egress/credentialed.ts is not the real blocker.

    Options:

    1. Write a durable row for each write-grant egress request (provider, grant, method, sanitized path, status, credential actor, delegated subject, binding/task id). This is the first slice of the action record itself.
    2. Add a narrow exemption so selected audit events reach Sentry at info level in production. This changes shared logging policy, and Sentry log retention is still not audit retention.
    3. Log write-grant egress at warn level. Not recommended: it misuses the level and adds noise to warnings.

    The first step (Automation version on runs) is in #2212.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions