Authentication
Section titled “Authentication”Add one comment. Your endpoint is protected. No middleware, no auth library, no JWT infrastructure. Hoody Exec’s @token magic comment adds shared-secret authentication to any script — HTTP and WebSocket.
// @token my-secret-key-123
return { data: 'only authenticated requests see this' };Unauthenticated requests get 401 Unauthorized. Authenticated requests run your script normally. That’s it.
How It Works
Section titled “How It Works”- You add
// @token <secret>at the top of your script - Every incoming request is checked for a matching token before your code runs
- If the token matches → script executes normally
- If the token is missing or wrong →
401 Unauthorized(your code never runs)
The token check happens at the server level, before VM creation and metadata construction. There is no way for a script to see or override the gate.
Credential Methods
Section titled “Credential Methods”Clients can provide the token four different ways. All are checked automatically — the first match wins.
Priority Order
Section titled “Priority Order”| Priority | Method | Header / Parameter |
|---|---|---|
| 1a | Bearer token | Authorization: Bearer <token> |
| 1b | Basic auth (password field) | Authorization: Basic base64(user:token) |
| 2 | X-Token header | X-Token: <token> |
| 3 | Query parameter | ?token=<token> |
If multiple sources are present, only the highest-priority one is used. For example, if both Authorization: Bearer and X-Token are sent, only the Bearer value is checked.
Bearer Token
Section titled “Bearer Token”The standard approach for API clients and SDKs.
curl -H "Authorization: Bearer my-secret-key-123" \ "https://PROJECT-CONTAINER-exec-1.SERVER.containers.hoody.icu/api/data"const res = await fetch('https://your-endpoint.containers.hoody.icu/api/data', { headers: { 'Authorization': 'Bearer my-secret-key-123' }});import requestsres = requests.get('https://your-endpoint.containers.hoody.icu/api/data', headers={'Authorization': 'Bearer my-secret-key-123'})The scheme name is case-insensitive (Bearer, bearer, BEARER all work).
Basic Auth
Section titled “Basic Auth”The @token value is matched against the password field of HTTP Basic auth. The username is completely ignored — send anything or nothing.
This means @token works out-of-the-box with:
curl -uflag- Browser native auth dialogs
- HTTP clients that only support Basic auth
- Legacy systems and webhook integrations
# Username "admin", password is the token — username is ignoredcurl -u admin:my-secret-key-123 \ "https://PROJECT-CONTAINER-exec-1.SERVER.containers.hoody.icu/api/data"# No username, just the token as passwordcurl -u :my-secret-key-123 \ "https://PROJECT-CONTAINER-exec-1.SERVER.containers.hoody.icu/api/data"# Any username works — only the password matterscurl -u monitoring-bot:my-secret-key-123 \ "https://PROJECT-CONTAINER-exec-1.SERVER.containers.hoody.icu/api/data"X-Token Header
Section titled “X-Token Header”A simple custom header — useful when Authorization is reserved by a proxy or gateway.
curl -H "X-Token: my-secret-key-123" \ "https://PROJECT-CONTAINER-exec-1.SERVER.containers.hoody.icu/api/data"Query Parameter
Section titled “Query Parameter”Pass the token in the URL. Convenient for quick testing, webhook callbacks, and browser links.
curl "https://PROJECT-CONTAINER-exec-1.SERVER.containers.hoody.icu/api/data?token=my-secret-key-123"Rejection Response
Section titled “Rejection Response”When authentication fails, the server returns:
HTTP/1.1 401 UnauthorizedWWW-Authenticate: Bearer realm="hoody-exec"Content-Type: application/json
{ "error": "Unauthorized", "message": "This endpoint requires authentication. Provide a valid token via Authorization: Bearer <token> header, X-Token header, ?token= query parameter, or HTTP Basic Auth."}WWW-Authenticate: Bearer— triggers native auth dialogs in browsers- Same response body whether the token is missing or wrong (no information leakage)
- If CORS is configured on the script (
@cors), CORS headers are included in the 401 response
CORS + Token
Section titled “CORS + Token”When using @token with @cors, CORS preflight requests (OPTIONS) are handled before the token check:
// @token my-secret-key-123// @cors reflective
return { data: 'protected + CORS-enabled' };Why: Browsers automatically send an OPTIONS preflight before cross-origin requests. Preflight requests cannot carry credentials — that’s the HTTP spec. If the server required a token on the preflight, CORS would be completely broken.
What happens:
- Browser sends
OPTIONSwithOrigin+Access-Control-Request-Method→ server returns204with CORS headers (no token required) - Browser sends the real
GET/POSTwithAuthorization: Bearer <token>→ server checks token, runs script
This is spec-correct behavior, not a bypass.
WebSocket Authentication
Section titled “WebSocket Authentication”The @token gate also protects WebSocket connections. The check runs on the HTTP upgrade request before the WebSocket handshake completes.
// @token my-secret-key-123// @mode worker// @websocket
ws.on('message', (socket, data) => socket.send('echo:' + data));Connect with token:
// Works in all WebSocket clients (including browsers)const ws = new WebSocket('wss://your-endpoint.containers.hoody.icu/ws?token=my-secret-key-123');// Node.js / Bun (browsers don't allow custom WebSocket headers)const ws = new WebSocket('wss://your-endpoint.containers.hoody.icu/ws', { headers: { 'Authorization': 'Bearer my-secret-key-123' }});Without a valid token, the server writes HTTP/1.1 401 Unauthorized to the socket and closes the connection. The WebSocket onerror / onclose event fires on the client.
Security Details
Section titled “Security Details”Constant-Time Comparison
Section titled “Constant-Time Comparison”Token comparison uses SHA-256 hashing on both sides followed by crypto.timingSafeEqual:
SHA-256(provided) === SHA-256(expected) // constant-timeThis prevents timing attacks — the comparison takes the same amount of time regardless of how many characters match.
Token Redaction
Section titled “Token Redaction”The token value is never exposed in any API response or log:
| Surface | Redaction |
|---|---|
| Access logs | ?token= replaced with ?token=[REDACTED] |
| Referer headers in logs | ?token= redacted |
Scripts API (/api/v1/exec/scripts/read) | Raw content shows // @token [REDACTED] |
| Magic Comments API | token field returns [REDACTED] |
metadata.parameters in your script | ?token= query param is removed before your code runs |
Encoded bypass attempts (%74oken=) | Caught by URL-parser fallback and redacted |
Token Isolation
Section titled “Token Isolation”When a client authenticates via ?token=, the token is immediately stripped from the request URL. Your script never sees it:
// @token my-secret
// Client calls: /api/data?token=my-secret&page=2// Your script sees:metadata.parameters // → { page: "2" } (no "token" key)metadata.path // → /api/data (clean)Examples
Section titled “Examples”Protect an API endpoint
Section titled “Protect an API endpoint”// @token sk_prod_a8f2e9c1d4b6// @cors reflective
const userId = metadata.parameters.id;const user = await db.getUser(userId);return user;Webhook receiver with token
Section titled “Webhook receiver with token”// @token whsec_github_abc123
if (req.method !== 'POST') { res.statusCode = 405; return { error: 'Method Not Allowed' };}
const payload = JSON.parse(await req.text());await processWebhook(payload);return { received: true };# GitHub webhook configured with:# URL: https://your-endpoint.containers.hoody.icu/webhooks/github?token=whsec_github_abc123Token + Worker mode + WebSocket
Section titled “Token + Worker mode + WebSocket”// @token realtime-secret-456// @mode worker// @websocket// @cors reflective
if (!shared.connections) shared.connections = new Set();
ws.on('open', (socket) => shared.connections.add(socket));ws.on('close', (socket) => shared.connections.delete(socket));ws.on('message', (socket, data) => { // Broadcast to all connected clients for (const client of shared.connections) { if (client !== socket) client.send(data); }});Programmatic management
Section titled “Programmatic management”# Read magic comments (token is redacted in response)hoody exec magic-comments read --path "api/data.ts"# → { "token": "[REDACTED]", "cors": "reflective" }const containerClient = await client.withContainer({ id: CONTAINER_ID, project_id: PROJECT_ID, server: SERVER});
// Read magic comments (token is redacted)const comments = await containerClient.exec.magic.read({ path: 'api/data.ts' });console.log(comments.data.comments); // { token: "[REDACTED]", cors: "reflective" }
// Update the tokenawait containerClient.exec.magic.updateHandler({ path: 'api/data.ts', comments: '{"token": "new-secret-key"}'});# Read magic comments (token is redacted in response)curl "https://PROJECT-CONTAINER-exec-1.SERVER.containers.hoody.icu/api/v1/exec/magic-comments/read?path=api/data.ts"# → { "comments": { "token": "[REDACTED]", "cors": "reflective" } }
# Update token via APIcurl -X PUT "https://PROJECT-CONTAINER-exec-1.SERVER.containers.hoody.icu/api/v1/exec/magic-comments/update" \ -H "Content-Type: application/json" \ -d '{ "path": "api/data.ts", "comments": "{\"token\": \"new-secret-key\"}" }'