Agent that calls Linear as itself. Keycard vaults the personal API key. The agent proves its identity with a signed JWT and never stores the Linear key.
This follows Grant Agent Access to APIs with Linear in place of Snowflake.
- An agent with its own identity. Its private key stays in
agent_keys/on this machine. - A Linear personal API key that lives in a Keycard vault, not in
.envor the repo. - One A2A request that returns the Linear user behind that key.
- An audit entry,
credentials:issue, recorded under the agent. See Reading the Audit Log & Sessions.
Keycard must download the agent’s public key from the public internet. Tailscale Funnel publishes only port 9000. The rest of this Mac stays private. Install Tailscale from the macOS download page.
- Open Tailscale and click Allow on the VPN and network-extension prompts. In System Settings, turn the Tailscale network extension on if macOS asks.
- Sign in. Everyday traffic still uses your normal connection. Do not turn on an exit node.
- In the Tailscale admin console, turn on MagicDNS, HTTPS certificates, and Funnel.
- Start the funnel:
tailscale funnel --bg 9000Copy the printed https://….ts.net URL into AGENT_BASE_URL in .env. No path and no port.
- When you are done for the day, stop the agent with Ctrl+C, then turn Funnel off:
tailscale funnel --https=443 offLeave Funnel off when you are not running the demo. While Funnel and the agent are both up, the A2A endpoint is public, and a request to it calls Linear with the vaulted key.
Do this once in Keycard Console before the first request. Details: Applications, Resources, and Credential issuance.
- Create a Linear personal API key. Linear → Settings → Account → Security & access. See Linear’s GraphQL docs.
- Create a resource named
Linear GraphQL API. Identifier:https://api.linear.app/graphql. Leave MCP Server off. Save, then Add credential and paste thelin_api_…key. The key is not a field on the create form. - Complete Tailscale setup and set
AGENT_BASE_URL. - Create an application for the agent. Set consent to Implicit so no one has to approve each call.
- Add a URL credential on that application. Identifier:
https://<your-funnel-host>/.well-known/oauth-client-metadata
- On the same application, add a dependency on the resource
https://api.linear.app/graphql. That link is what allows Keycard to hand this agent the vaulted key.
- Copy
.env.exampleto.env. SetAGENT_BASE_URLto the Tailscale Funnel URL. - Vault the Linear key on resource
https://api.linear.app/graphqland attach it to this agent. - Start the funnel, then the agent:
tailscale funnel --bg 9000
keycard run -- npm start- Ask who you are:
curl -X POST http://localhost:9000/a2a/jsonrpc \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"tasks/send","id":"1","params":{"id":"task-1","message":{"role":"user","parts":[{"kind":"text","text":"Who am I in Linear?"}]}}}'Done when the reply includes your Linear name and Keycard Audit Log shows credentials:issue for https://api.linear.app/graphql.