Inspiration

AI agents can read files, call tools, and communicate with other users. However, the model itself should not decide whether it is authorized to perform these actions.

A prompt saying “do not share this” is not a reliable security boundary. Instructions inside a document may also influence an Agent’s behavior. If the backend does not enforce permission checks, an approval screen in the frontend has little security value.

We built Agent Bouncer to explore one question:

What evidence would help users trust an Agent when it works with private data?

Our approach is not to treat model output, browser state, Runtime files, or document content as sources of authorization. Protected operations must depend on user decisions, narrowly scoped permissions, and execution records produced by a trusted backend.

What it does

Agent Bouncer is authorization middleware for Agent Launchpad. It separates the human who initiates a Run from the Agent executing it, then applies backend checks to private-resource operations managed by the platform.

Its authorization process is progressive:

  • Attaching an owned resource allows the Agent to read and answer questions about that exact resource during the current Run.
  • Without an attachment, the Agent must first request metadata-only access to the user’s private resource catalog.
  • Catalog approval returns titles, kinds, and creation times, but not file contents.
  • Reading or displaying resource content in the current conversation requires a separate action-specific approval.
  • Forwarding requires authorization for the exact resource and recipient.
  • Alice can approve resources that she owns, but she cannot approve Bob’s private resources.
  • Instructions found inside a resource cannot create forwarding authorization on behalf of the user.
  • After an approved forward, the backend delivers the protected content directly to the human-to-human conversation. The Agent Runtime receives only a delivery receipt.

If a resource contains an instruction telling the Agent to forward it to Bob, the Agent may still attempt to follow that instruction. However, the embedded text alone cannot complete the transfer. The system requests an explicit decision from the resource owner, and if the owner rejects it, the platform-managed forward does not occur.

Authorization decisions record the initiating human, executing Agent, Run, action, target, allow or deny result, reason code, and policy version. An Agent claiming that it requested or completed an operation cannot replace the corresponding backend record.

How we built it

The project uses a functional execution path:

React Web UI → Fastify API → AgentService → disposable Agent Runtime → protected resource gateway → policy evaluation → audit records

The backend identifies the current user through an HttpOnly, SameSite=Strict session instead of trusting a user ID submitted by the browser.

Each Run receives a short-lived Runtime credential bound to the human, Agent, Run, conversation, and optional task. The Runtime requests protected operations through a generated vault.mjs tool, but the tool itself cannot create authorization.

Catalog access, reading, restricted processing, disclosure in the current conversation, and external forwarding are evaluated as separate policy actions. Approval requests are persisted, and the Run enters waiting_for_approval. After the user makes a decision, the workflow continues and requires final evidence for the corresponding protected operation.

Forwarding is also bound to a specific recipient. After approval, the backend delivers the resource to the human-to-human conversation and returns only a receipt to the model, reducing the number of times protected content needs to enter the Agent Runtime.

The frontend provides:

  • Action-specific approval cards
  • Agent and Run lifecycle states
  • A live Backend Execution Trace
  • Human-readable authorization records
  • Waiting, rejection, expiry, and completion states
  • A persistent Chinese/English interface switch

The language switch changes fixed UI text only. It does not modify user messages, Agent output, protected content, commands, API paths, or audit reason codes.

Challenges we ran into

The main challenge was making approval affect the actual Run lifecycle instead of behaving like a decorative frontend modal.

We needed to handle situations where:

  • The user completes an approval before the current Agent turn exits
  • A Run must not be treated as successful merely because an approval was granted
  • The final result must contain backend evidence for the protected operation that was approved
  • Private catalog metadata must not be exposed without approval
  • Inaccessible and nonexistent titles should produce the same Runtime-facing error
  • Reading, conversational disclosure, and external forwarding must be evaluated separately
  • A forward must be bound to the exact resource and recipient
  • Document content must not be treated as a user forwarding instruction
  • Audit information must remain understandable without storing credentials or protected content

This showed us that human approval is not only a button. It is a set of persisted and verified state transitions.

Accomplishments that we're proud of

  • A complete authorization path from the frontend to the backend and Agent Runtime
  • Separate catalog, read, disclosure, and forwarding permissions
  • Metadata-only catalog approval that does not automatically grant content access
  • Recipient-bound resource forwarding
  • Backend denial when Alice requests Bob’s private resource
  • Human approval requirements for forwarding attempts triggered by embedded document instructions
  • Run-linked and redacted authorization records
  • Approval, rejection, timeout, cancellation, and completion state handling
  • A requirement for backend policy evidence rather than Agent text alone
  • One-command isolated demo startup with npm run demo:fresh
  • 107 automated tests covering policy, lifecycle, approval races, denial paths, evidence, and API boundaries
  • Passing TypeScript checks, automated tests, and production builds
  • No known dependency vulnerabilities reported by npm audit at the time of submission

What we learned

Agent authorization should be based on trusted backend state and narrowly scoped capabilities, rather than prompts, model output, browser claims, or instructions found inside documents.

We also learned that “approved” does not mean “completed.” The system still needs a final operation record corresponding to what was approved.

For a rejected operation, it is not enough to display a rejection message. The platform should ensure that the protected delivery did not occur and preserve evidence of that decision.

Agent Bouncer protects operations that pass through the platform’s protected-resource interfaces. It is not a general prompt-injection detector, and it does not replace complete Runtime network isolation or a production data-loss-prevention system.

What's next for Agent Bouncer

Agent Bouncer is currently a hackathon-scale POC using seeded accounts, a single-process JSON store, and disposable local containers.

Possible next steps include:

  • Replacing the JSON store with transactional persistent storage
  • Integrating production identity, registration, MFA, and organization management
  • Storing model credentials in a managed secret service
  • Restricting Runtime network egress
  • Strengthening multi-tenant workload isolation
  • Supporting centralized policy administration and delegated approval
  • Exporting authorization records to production audit and security-monitoring systems
  • Adding policy simulation and administrator review workflows

The goal of Agent Bouncer is not to solve every Agent security problem. It provides a functional and auditable backend-authorization example for private-resource access and forwarding.

Built With

Share this project:

Updates

Submission history