Skip to content

Feature request: code pinning for permits #2

@amiller

Description

@amiller

Problem

When a permit is approved, the client (requester) can supply arbitrary code at execution time via POST /execute { code: "..." }. The permit's scoped-fetch capabilities constrain which URLs and methods are reachable, but the code itself is unconstrained.

This makes permits unsuitable for scenarios where the owner wants to delegate a narrow, specific action — not just access to a URL scope. For example:

Use case: A web page lets users claim an NFT by creating a GitHub issue. The page calls OAuth3 to create the issue on behalf of users who don't have GitHub accounts. The permit should only allow creating issues with a specific format (title matching [PAPER] Claim paper #N, body containing paper_id, email_hash, recipient). But because the client controls the code, anyone with the embedded API key can create arbitrary issues or post arbitrary comments on any issue in the repo.

The URL scope (repos/owner/repo/issues) is too coarse — it can't distinguish "create a well-formed claim issue" from "create a spam issue" or "post arbitrary comments."

Proposed solution: code pinning

Allow the permit to include a code field that is fixed at creation/approval time. When a pinned-code permit is executed, the client can only supply args — the code itself cannot be overridden.

POST /permit
{
  "description": "FC26 NFT claim relay",
  "code": "const papers = await papers('GET', 'papers.json'); ...",
  "capabilities": [ ... ]
}

Then at execution time:

POST /execute
{
  "permit_id": "permit_xxx",
  "args": { "email": "...", "paper_id": 14, "recipient": "0x..." }
}

The code field in /execute would be rejected (or ignored) when the permit has pinned code.

Why this matters

Without code pinning, the security model for permits is:

  • What the owner thinks they're approving: "This permit lets the client create claim issues for my NFT project"
  • What they're actually approving: "This permit lets the client run any JavaScript that can POST to repos/owner/repo/issues"

These are very different threat models. The owner has no way to constrain the business logic, only the network scope.

Context

This came up while building a TEE relay for FC26 rump session NFTs. The integration worked end-to-end but was reverted because the permit scope was too broad without code pinning. The sandbox code validated emails against a hash list before creating issues, but since any client can replace that code, the validation provided zero security.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions