← Receivers

secrets:grant

secrets family

Lend a key you gave to ONE, or accept one lent to you. LEND: a signed grant — who may use the key (a person, an agent, or a group), which calls (catalogue entries of the provider row), how many a day, until when — checked against the grant-signers of the vault the key was given under (never the vault the document names), and stored only once its signature verifies (an id is never reused on a vault). The borrower gets a card linking one.ie/keys?lent=<ref>; their calls run on the key once they accept, and they never see it. ACCEPT: `accept` names the grant's ref; `confirm: false` shows it in words and writes nothing, `confirm: true` records the acceptance. Only a person's own session accepts — for themselves, or with `group` / `agent` for a group or an agent they control — never an agent, and never from a body field. Closes with a mark on `actor → provider:<name>`, a warn on a refusal once the provider is known.

Effect
ask
Awaits an outcome — the call returns the response below.
Caller
authenticated
The class of authority the caller must already hold, decided from the attested context with no round trip.
Cost
free
What one call costs, so you can budget before acting.
Reversible
no
This cannot be undone. Dry-run it first where the contract allows.
Idempotent
yes
Safe to retry as-is.

Send it with your agent

One click hands your coding agent a prompt that registers the substrate, reads this contract, and makes the call. Launch opens the app; the others copy the prompt.

Claude Code
Codex
Cursor
Gemini CLI
Claude Desktop
ChatGPT
curl -X POST https://one.ie/api/ask/secrets:grant \
  -H "Authorization: Bearer $ONE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"data": { "accept": "<64 hex ref from the card>", "confirm": false }}'

The key is never in a link. npx -y @oneie/cli login writes it to ~/.config/oneie/key on your machine.

Request

Validated before dispatch — an invalid payload is refused with the fix, never half-applied.

  • signer object optional LEND form only.
  • payload object optional LEND form only.
  • accept string optional ACCEPT form: the grant's ref (from the card's link).
  • confirm boolean optional ACCEPT form: false shows the grant in words and writes nothing; true records the acceptance (once).
  • group string optional ACCEPT form: accept for a group you control (owner or admin).
  • agent string optional ACCEPT form: accept for an agent whose home group you control. An agent never accepts for itself.

Response

What comes back from the call.

  • ok boolean
  • grant object LEND: what was stored. `ref` is sha256 of the signed bytes — the card links it.
  • existing boolean The identical grant (or acceptance) was already there — nothing new was written and no second card sent.
  • accepted boolean
  • offer object ACCEPT with confirm false: the grant in words — never the key, the sealed blob or a signature.
  • provider string
  • refused string The opener's: malformed · expired · no_key · bad_template · bad_signature · cannot_verify · busy · id_used · no_grant · not_addressee.
  • detail string
  • error string The web side: unauthorized · not_in_workflow · not_allowed (an accept not made by a person's own session, or for a group or agent the caller does not control) · bad_request · opener_unbound · opener_unreachable.

Traffic

Every call to secrets:grant, counted where it is dispatched — over HTTP or in-process alike. Aggregate only — no actor, no payload, no workspace.

Counting…

Wiring

Every place in the open source that names secrets:grant, and the file that answers it. Read from the tree at build time — a receiver is reached by NAME through one door, so there is no import edge to follow and a grep is the honest shape of the question. Structure, not volume — the count is in Traffic above.

Called from

UI 2
  • one.ie/web/src/components/key/LendSheet.tsx:14
  • one.ie/web/src/components/key/LentToMe.tsx:4
API route 2
  • one.ie/web/src/lib/receiver-index.generated.ts:327
  • one.ie/web/src/lib/secrets/grant.ts:5
SDK 1
  • packages/sdk/src/secrets-grant.ts:63
→

Answered by

secrets:grant one.ie/web/src/lib/resolvers/secrets.ts:496 Dispatched through POST /api/ask/secrets:grant, after the envelope validates the payload.
JSON Schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "signer": {
      "description": "LEND form only.",
      "type": "object",
      "properties": {
        "kind": {
          "type": "string",
          "enum": [
            "person",
            "agent"
          ],
          "description": "person: a passkey touch on the seal page, in a window of its own (a framed grant is refused). agent: its own ed25519 Sui key (the SDK's grantData)."
        },
        "sig": {
          "type": "string",
          "description": "The signature over the payload's canonical bytes (keys sorted, @oneie/sdk/secrets)."
        },
        "signer": {
          "type": "string",
          "description": "Who signed: `p256:<point>` for a passkey, the agent's Sui address. It must be on the key's vault's grant-signers, read from the chain."
        }
      },
      "required": [
        "kind",
        "sig",
        "signer"
      ]
    },
    "payload": {
      "description": "LEND form only.",
      "type": "object",
      "properties": {
        "type": {
          "type": "string",
          "const": "grant"
        },
        "vault": {
          "type": "string",
          "description": "The vault the key was given under, `0x` + 64 lower-case hex — the same as body.vault."
        },
        "body": {
          "type": "object",
          "properties": {
            "id": {
              "type": "string",
              "description": "8–64 of [A-Za-z0-9_-]; mint 16 random bytes as hex (grantData does). Never reused on a vault: a second grant naming a seen id is refused id_used, so a Stop is permanent."
            },
            "secret": {
              "type": "string",
              "description": "`<vault>/<NAME>` — the key as it was given. A secret never given is refused no_key before anything reaches the chain."
            },
            "vault": {
              "type": "string"
            },
            "to": {
              "type": "object",
              "properties": {
                "kind": {
                  "type": "string",
                  "enum": [
                    "actor",
                    "group"
                  ]
                },
                "id": {
                  "type": "string",
                  "description": "An actor id, or a group's bare slug (never `group:`-prefixed)."
                },
                "agents": {
                  "description": "Only on a grant to a person: the agents of theirs it also covers. A grant to a person covers their agents only when it names them.",
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                }
              },
              "required": [
                "kind",
                "id"
              ]
            },
            "for": {
              "type": "object",
              "properties": {
                "paths": {
                  "description": "Catalogue entry ids of the key's provider row, bare or `<id>@/concrete/path` (one repo, one issue). Absent: the row's default reads. []: nothing. Any other item refuses bad_template.",
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                },
                "models": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                },
                "tools": {
                  "description": "Composio tool slugs — only on a key whose stored provider is composio, never a COMPOSIO_ meta-tool.",
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                }
              }
            },
            "perDay": {
              "type": "integer",
              "minimum": 1,
              "maximum": 9007199254740991,
              "description": "Calls a day, counted before each call."
            },
            "until": {
              "type": "string",
              "description": "ISO-8601 UTC ending Z, in the future. No upper cap: the provider's own budget is the honest one."
            }
          },
          "required": [
            "id",
            "secret",
            "vault",
            "to",
            "for",
            "perDay",
            "until"
          ]
        }
      },
      "required": [
        "type",
        "vault",
        "body"
      ]
    },
    "accept": {
      "description": "ACCEPT form: the grant's ref (from the card's link).",
      "type": "string",
      "pattern": "^[0-9a-f]{64}$"
    },
    "confirm": {
      "description": "ACCEPT form: false shows the grant in words and writes nothing; true records the acceptance (once).",
      "type": "boolean"
    },
    "group": {
      "description": "ACCEPT form: accept for a group you control (owner or admin).",
      "type": "string"
    },
    "agent": {
      "description": "ACCEPT form: accept for an agent whose home group you control. An agent never accepts for itself.",
      "type": "string"
    }
  }
}
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "ok": {
      "type": "boolean"
    },
    "grant": {
      "description": "LEND: what was stored. `ref` is sha256 of the signed bytes — the card links it.",
      "type": "object",
      "properties": {
        "ref": {
          "type": "string"
        },
        "id": {
          "type": "string"
        },
        "secret": {
          "type": "string"
        },
        "provider": {
          "type": "string"
        },
        "to": {},
        "perDay": {
          "type": "number"
        },
        "until": {
          "type": "string"
        }
      },
      "required": [
        "ref",
        "id",
        "secret",
        "provider",
        "to",
        "perDay",
        "until"
      ]
    },
    "existing": {
      "description": "The identical grant (or acceptance) was already there — nothing new was written and no second card sent.",
      "type": "boolean"
    },
    "accepted": {
      "type": "boolean"
    },
    "offer": {
      "description": "ACCEPT with confirm false: the grant in words — never the key, the sealed blob or a signature.",
      "type": "object",
      "properties": {
        "ref": {
          "type": "string"
        },
        "provider": {
          "type": "string"
        },
        "name": {
          "type": "string"
        },
        "perDay": {
          "type": "number"
        },
        "until": {
          "type": "string"
        },
        "to": {},
        "paths": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "models": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "tools": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "from": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ]
        }
      },
      "required": [
        "ref",
        "provider",
        "name",
        "perDay",
        "until",
        "to",
        "from"
      ]
    },
    "provider": {
      "type": "string"
    },
    "refused": {
      "description": "The opener's: malformed · expired · no_key · bad_template · bad_signature · cannot_verify · busy · id_used · no_grant · not_addressee.",
      "type": "string"
    },
    "detail": {
      "type": "string"
    },
    "error": {
      "description": "The web side: unauthorized · not_in_workflow · not_allowed (an accept not made by a person's own session, or for a group or agent the caller does not control) · bad_request · opener_unbound · opener_unreachable.",
      "type": "string"
    }
  },
  "required": [
    "ok"
  ]
}