Skip to main content
Security is a first-class concern in RemoteAgent’s design. The most important property is also the simplest: your API key never leaves your machine.

The relay model

RemoteAgent is a pure relay. It routes messages between Telegram and your local agent. It does not:
  • Store your prompts or AI responses (only a 200-character preview for the session list)
  • Read your source code
  • See your Anthropic, OpenAI, or any other AI API key
  • Execute code on your behalf
Your AI runner calls the model API directly from your machine, using credentials stored only in ~/.remoteagent/config.json. The RemoteAgent servers see a Redis message containing the prompt you sent via Telegram — they do not process it, interpret it, or log it beyond the preview.
Do not include sensitive secrets in your Telegram commands. While the relay does not log prompts, treat Telegram as you would any messaging app — appropriate for instructions, not for embedding credentials.

API key storage

API keys are stored locally in:
  • ~/.remoteagent/config.json — global config (e.g., Anthropic API key)
  • ~/.remoteagent/agents/{agentId}.json — per-agent config
These files are written by remoteagent init and read only by the local agent process. They are never uploaded, synced, or referenced by any network request made by the relay. Recommended file permissions:
chmod 700 ~/.remoteagent
chmod 600 ~/.remoteagent/config.json
chmod 600 ~/.remoteagent/agents/*.json

Agent authentication (JWT)

When you complete pairing, the server issues a JWT (signed with a secret known only to the server). This token is stored in the per-agent config and used to authenticate the agent’s Redis subscriptions. The JWT:
  • Identifies the agent by its agentId
  • Scopes the agent’s access to its own Redis channels only
  • Does not grant access to other users’ agents or data
  • Can be revoked by deleting the agent from the dashboard

Telegram webhook verification

Every POST request to /api/telegram is verified using HMAC-SHA256 before any processing occurs. The signature is computed using the TELEGRAM_WEBHOOK_SECRET (set in Vercel environment variables) and compared against the X-Telegram-Bot-Api-Secret-Token header Telegram sends with each request. Requests that fail signature verification are rejected with a 401 response before Grammy.js processes them.

Telegram user authorization

The bot checks the Telegram user ID of every incoming message against the list of authorized users for your account. Messages from unauthorized user IDs are silently ignored — no error response is sent, which avoids leaking information about which user IDs are registered. This check runs before any command is published to Redis, so unauthorized messages never reach your agent.

Pairing code security

Pairing codes are:
  • Short-lived — expire after 10 minutes
  • Single-use — marked as used immediately on first redemption; replaying the same code returns an error
  • Unpredictable — generated with cryptographically secure randomness
  • Not reusable — deleted from the database after use
The pairing flow requires the user to authenticate with the Telegram Login Widget before entering the code. This binds the agent to a verified Telegram user ID, not just anyone who happens to have the code.

Redis channel isolation

Each agent subscribes only to its own channel (agent:{agentId}). The agent ID is derived from the JWT payload, which is verified on every subscription. An agent cannot subscribe to another agent’s channel. Output channels (output:{sessionId}) are keyed by session ID, which is generated server-side and unknown to other users.

Stripe webhook verification

Every POST to /api/stripe/webhook uses stripe.webhooks.constructEvent() with the webhook signing secret from Stripe Dashboard. This verifies that the event genuinely originated from Stripe and has not been tampered with. Plan updates are only applied after a verified Stripe webhook event — never based on redirect URLs, client-side callbacks, or untrusted inputs.

Rate limiting

The /api/telegram endpoint enforces rate limiting per Telegram user ID: a maximum of 10 requests per minute. Requests exceeding this limit receive a 429 response. This prevents abuse of the relay by scripts or bots flooding the system with commands.

Summary

Security controlImplementation
API key isolationStored locally, never transmitted
Agent authenticationJWT, server-signed, scoped per agent
Telegram webhook integrityHMAC-SHA256 on every request
User authorizationTelegram user ID check before every command
Pairing code10-min TTL, single-use, cryptographically random
Redis channel isolationChannels keyed by agentId, JWT-verified
Stripe webhook integrityconstructEvent() with signing secret
Rate limiting10 req/min per Telegram user