Skip to content

AI Guard Policies

AI Guard applies policies to stop or limit the behavior Panopticon observes. We are designing four policies in the beta.

🔬 Some policies are at the design stage

Of the four policies this page covers, the HITL approval endpoint and the Soft Kill Switch CLI are not yet implemented in the codebase (audited 2026-05-15). Even if you define a policy in the start.1pass.dev/developer console, the logi server does not yet provide:

  • POST /panopticon/approval/request — not implemented (404)
  • GET /panopticon/approval/<request_uuid> — not implemented (404)
  • logi panopticon kill <client_id> CLI — not implemented
  • Kill-switch state columns such as OauthApplication#killed_at — not implemented

Policies that work today: Scope Drift log_only (the default), and Rate Limit (the rack-attack throttle on /oauth/token and /oauth/userinfo). The rest can be defined in the console UI but are not wired to enforcement. For status inquiries, reach us at support@1pass.dev.

Policies at a glance

PolicyImmediacyRP-side burdenStatus
Rate Limit (rack-attack /oauth/*)Immediate (server)None✓ Implemented
Rate Limit (per-application × tool)Immediate (server)Receive webhook🔬 In design
Soft Kill Switch≤ 15 minNone🔬 In design
HITL approvalWhen the user respondsExtra endpoint call🔬 In design
Scope Drift Policy log_onlyImmediateNone✓ Implemented
Scope Drift Policy alert / blockImmediate (/oauth/authorize)None🔬 In design

In the beta, the implemented policies are configured and enforced, but quota hard-blocking and billing are disabled.


1. Rate Limit

Defends against a flood of tool calls caused by LLM hallucination.

There are two kinds of Rate Limit, applied at different points.

A. logi endpoint throttle (existing rack-attack)

Protects the endpoints logi hosts directly, such as /oauth/token and /oauth/userinfo. Per application/IP.

B. AI Guard tool throttle (new in Panopticon)

For an application that reports traces, Panopticon counts (application × user) and (application × tool) usage on the logi side against these limits. When a limit is exceeded, logi fires a webhook (panopticon.anomaly_detected), and the RP decides whether to block or warn based on its own policy. logi does not block the tool call itself — the RP must receive the webhook and block on its own side.

Defaults (per application × user):

  • 60 calls per minute
  • 20 calls per minute for the same tool

Override (console or CLI):

bash
logi panopticon policy <client_id> --rate-limit '{"per_minute":120,"per_tool_per_minute":40}'

2. Soft Kill Switch 🔬 (design stage)

Not implemented — design spec

The trigger procedures below (CLI · console 1-click) have no route on the logi server yet. The OauthApplication#killed_at column, the logi panopticon kill CLI command, and the console Kill Switch tab are all in the backlog.

Blocking measures available today:

  • Refresh token rotation + reuse detection: already implemented (Oauth::TokensController#rotate_refresh_token + OauthAccessToken#revoke_chain!). On reuse detection, the entire chain is revoked automatically, so in a theft scenario this effectively blocks immediately.
  • Manual DB revoke: run OauthAccessToken.where(oauth_application_id: APP_ID).find_each(&:revoke!) in the console. Once the RT is revoked, the next rotation attempt is rejected.

Immediate blocking of a specific application or (application × user).

Trigger (planned)

Console → application → Kill Switch tab → 1-click from the user matrix

Or the CLI:

bash
logi panopticon kill <client_id>                    # the whole application
logi panopticon kill <client_id> --user user_xyz    # a specific user only
logi agent revoke <client_id>                       # ALIAS for kill

Behavior (planned)

  1. Revoke all refresh tokens of the given OauthAccessToken.
  2. Existing access tokens expire naturally within at most 15 minutes (TTL 15 minutes).
  3. Fire the panopticon.kill_switch webhook.

If you want immediate blocking

The RP calls /oauth/introspect on every call (latency is on you, opt-in). The default RP accepts the 15-minute delay.


3. HITL approval (opt-in for sensitive tools) 🔬 (design stage)

Not implemented — no route

The POST /panopticon/approval/request and GET /panopticon/approval/<request_uuid> below do not exist in server/config/routes.rb (audited 2026-05-15). Calling the sample code as-is returns a 404.

Only PATCH /api/v1/me/panopticon/approvals/:id (user-session auth) partially exists — it is not an RP-facing PAK-auth API, so you cannot use it for RP integration.

A specific tool runs only after the user grants push approval in their logi app.

Configuration

bash
logi panopticon policy <client_id> --hitl payment.charge,memory.delete,calendar.delete

Or console → Policies → add the tool name to the HITL tool list.

RP-side flow

python
# right before running the tool
async def call_payment_tool(amount, user_sub):
    # 1. request approval from logi
    resp = await httpx.post(
        f"{LOGI_URL}/panopticon/approval/request",
        headers={"Authorization": f"Bearer {PANOPTICON_PAK}"},
        json={
            "tool_name": "payment.charge",
            "tool_args_summary": {"amount": amount, "currency": "KRW"},
            "user_sub": user_sub,
        }
    )
    request_uuid = resp.json()["request_uuid"]

    # 2. wait for the user's response (polling or webhook)
    while True:
        status = await poll_approval(request_uuid)
        if status == "approved":
            return await execute_payment(amount)
        elif status in ("denied", "expired"):
            raise PermissionError(status)
        await asyncio.sleep(2)

The poll endpoint:

http
GET /panopticon/approval/<request_uuid>
Authorization: Bearer pano_pak_...

Response: {"status":"pending|approved|denied|expired","approved_at":"..."}

TTL: 5 minutes. After it expires, it returns expired.

User experience

  1. When the RP calls the tool, a push notification arrives in the user's logi app.
  2. "MyAgent is requesting to run the payment tool — approve?"
  3. The user approves or denies in the logi app (or at /auth/approve/<uuid> via a Universal Link).
  4. The result is delivered to the RP by polling or webhook.

4. Scope Drift Policy

How to handle the case where the LLM requests a scope that is not registered.

The 3-level policy

PolicyBehaviorRecommended for
log_only (default)The existing behavior: record the drift + scope.drift_detected webhook + proceed with the registered scope subsetGeneral applications
alertlog_only + show an inline alert card in the consoleApplications under active monitoring
blockWhen an unregistered scope is found at /oauth/authorize, return 400 invalid_scope (rejecting the authorization itself, RFC 6749 §4.1.2.1)Security-sensitive applications

Changing it

bash
logi panopticon policy <client_id> --drift block

Or console → Policies → the Scope Drift Policy radio.

Every Scope Drift is recorded

Regardless of policy, ScopeDriftRecord ingests every drift event. On the first occurrence, a scope.drift_detected webhook fires; after escalation, a scope.drift_unresolved webhook fires (see the Webhook guide).


How usage relates to policy

During the beta, policy enforcement is separate from quota enforcement — policies enforce their own behavior, but no quota limit is applied. That is:

  • Rate Limit: throttles when the per-minute limit is exceeded (applied).
  • Soft Kill: only when the user runs it (no automatic trigger).
  • HITL: user approval is required (applied).
  • Scope Drift block: rejects on an invalid scope (applied).

The quota policies to be added after GA (monthly trace limit, per-tier differentiation) are covered in a separate spec.

Next

Identity가 제품의 신뢰를 만듭니다.