
Company News
Andrew Becherer Joins Socket as Chief Information Security Officer
Socket’s first CISO brings deep experience securing high-growth SaaS companies as open source supply chain threats accelerate.
Authentication middleware for the SuperInstance fleet — API keys, JWTs, and service bindings, purpose-built for Cloudflare Workers.
Every fleet worker needs to answer the same question: who is calling, and are they allowed to? This crate answers it. It provides a single withAuth() middleware that handles API keys (fleet_sk_...), HS256 JWTs, and Cloudflare service-binding detection — so your worker handler only sees authenticated requests with granted scopes.
The SuperInstance fleet has 20+ Cloudflare Workers communicating over HTTP. Every worker needs auth, but every worker shouldn't reimplement it. fleet-auth is the shared authentication layer that:
read:vector, write:fishinglog, or wildcard *The design targets sub-5ms auth overhead. KV lookups for API keys hit in 1–3ms. JWT verification is pure CPU (no I/O). Service binding detection is a single header check.
Incoming Request
│
┌─────────▼─────────┐
│ withAuth() │
│ middleware │
└─────────┬─────────┘
│
┌───────────▼───────────┐
│ Service binding? │──── Yes ──► Trust (CF runtime guarantees auth)
│ (CF-Connecting-Source)│
└───────────┬───────────┘
│ No
┌───────────▼───────────┐
│ Extract Bearer token │
└───────────┬───────────┘
│
┌───────────────┼───────────────┐
│ │ │
┌─────────▼──────┐ ┌────▼─────┐ ┌──────▼──────┐
│ fleet_sk_... │ │ JWT │ │ Unknown │
│ (API key path) │ │ (3-part) │ │ │
└─────────┬──────┘ └────┬─────┘ └──────┬──────┘
│ │ │
┌─────────▼──────┐ ┌────▼─────┐ │
│ KV lookup │ │ HS256 │ │
│ (fallback D1) │ │ verify │ │
│ SHA-256 compare│ │ (rotate) │ │
└─────────┬──────┘ └────┬─────┘ │
│ │ │
└───────┬───────┘ ┌────────▼────────┐
│ │ 401 Unauthorized │
┌───────▼───────┐ └─────────────────┘
│ Scope check │
│ (wildcard ok) │
└───────┬───────┘
│
┌──────────▼──────────┐
│ Your handler(req, │
│ env, ctx, auth) │
└─────────────────────┘
| Decision | Rationale |
|---|---|
| Dual auth (API key + JWT) | API keys for long-lived service accounts; JWTs for short-lived user sessions |
| SHA-256 hashed keys | Never store plaintext secrets. Keys are hashed on creation, compared by hash on validation. |
| KV mirror + D1 source of truth | KV for hot-path latency (1–3ms); D1 for durability and complex queries |
| JWT secret rotation | JWT_SECRET_CURRENT + JWT_SECRET_PREVIOUS enables zero-downtime key rotation |
| Audit to D1 (non-blocking) | ctx.waitUntil() ensures audit logging never blocks the response |
| Service binding trust | CF runtime authenticates internal requests — no token needed |
# In your Cloudflare Worker project
cp -r ../fleet-auth/src/auth ./src/auth
# Or import directly if published
import { withAuth } from "./auth/middleware";
export default {
fetch: withAuth(["read:vector"], async (req, env, ctx, auth) => {
// auth.keyId — who's calling
// auth.scopes — what they can do
// auth.via — "api-key" | "jwt" | "service-binding"
return Response.json({ ok: true, you: auth.keyId });
}),
};
import { generateApiKey } from "./auth/keys";
const result = await generateApiKey(env, {
name: "fleet-scanner-service",
owner: "ci-pipeline",
scopes: ["read:vector", "read:fleet"],
expiresIn: 30 * 24 * 60 * 60 * 1000, // 30 days
});
console.log("Key (save this — shown once):", result.key);
console.log("Key ID:", result.keyId);
// Key format: fleet_sk_<8-char-base32-id>.<44-char-base64url-secret>
import { sign } from "./auth/jwt";
const token = await sign(
{
iss: "fleet-auth",
sub: "user-123",
aud: "fleet-vector-api",
scopes: ["read:vector", "write:fishinglog"],
kid: "key-1",
},
env.JWT_SECRET_CURRENT,
);
// token is a standard HS256 JWT, expires in 15 minutes
withAuth(requiredScopes, handler, options?)Wrap a Workers fetch handler with authentication.
withAuth(
["read:vector", "write:fishinglog"], // all must be present
async (req, env, ctx, auth) => { /* your handler */ },
{ allowServiceBinding: true, skipAudit: false }
);
hasScopes(granted, required)Check scope satisfaction. Supports wildcards:
"*" matches everything"read:*" matches "read:vector", "read:fleet", etc.| Function | Description |
|---|---|
generateApiKey(env, options) | Create key, store hash in D1, mirror to KV. Returns full key (once!). |
validateApiKey(token, env) | KV-first lookup, D1 fallback. SHA-256 comparison. |
rotateApiKey(env, oldKeyId, options) | Create replacement key. Old key stays valid during grace period. |
revokeApiKey(env, keyId) | Immediate revocation. Deletes from KV, marks revoked in D1. |
| Function | Description |
|---|---|
sign(payload, secret) | Sign a JWT with HS256. 15-minute TTL. |
verify(token, secrets) | Verify against current + previous secrets (rotation support). |
decode(token) | Decode without verifying (debugging only). |
shouldRenew(token, withinSeconds?) | Check if token expires soon. Default: within 120s. |
fleet_sk_abcdefghij.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
│ │ ──────────────────────────────────────────────
│ │ 32-byte random secret (base64url)
│ └── 8-char base32 key ID (used for lookup)
└── prefix
The key ID (abcdefghij) is the public identifier. It's used for KV lookups, D1 queries, and audit logs. The secret is the private half — it's stored as a SHA-256 hash, never in plaintext. When you validate, you hash the submitted secret and compare.
This is the same pattern as Stripe API keys (sk_live_...) and GitHub tokens (ghp_...).
Scopes follow the verb:resource pattern:
| Scope | Description |
|---|---|
read:vector | Read vector embeddings |
write:fishinglog | Write fishing log entries |
read:fleet | Read fleet status |
* | Full access (service bindings get this) |
read:* | Read access to all resources |
Your Worker needs these bindings in wrangler.toml:
[[d1_databases]]
binding = "DB"
database_name = "fleet-auth-db"
database_id = "your-database-id"
[[kv_namespaces]]
binding = "AUTH_KV"
id = "your-kv-namespace-id"
[vars]
WORKER_NAME = "your-worker-name"
# Set via `wrangler secret put`:
# JWT_SECRET_CURRENT — current signing secret
# JWT_SECRET_PREVIOUS — previous secret (for rotation)
The api_keys and auth_audit tables must exist. Deploy from fleet-events-db migrations:
-- api_keys table
CREATE TABLE api_keys (
key_id TEXT PRIMARY KEY,
key_hash TEXT NOT NULL,
name TEXT NOT NULL,
owner TEXT NOT NULL,
scopes TEXT NOT NULL, -- JSON array
status TEXT NOT NULL, -- 'active', 'rotating', 'revoked'
created_at INTEGER NOT NULL,
expires_at INTEGER,
last_used_at INTEGER,
rotated_from TEXT
);
-- auth_audit table
CREATE TABLE auth_audit (
ts INTEGER NOT NULL,
key_id TEXT,
worker TEXT NOT NULL,
path TEXT NOT NULL,
outcome TEXT NOT NULL, -- 'ok', 'invalid', 'expired', 'revoked', 'scope_denied', 'malformed'
ip TEXT
);
Zero-downtime secret rotation:
JWT_SECRET_PREVIOUS = current_secretJWT_SECRET_CURRENT = new_secretPREVIOUS)JWT_SECRET_PREVIOUSEvery authenticated request follows the fleet's conservation law (γ + η = C):
The audit trail is append-only, like the fleet's event sourcing in fleet-events-db. Auth events are first-class fleet events.
api_keys and auth_audit tablesMIT
FAQs
Authentication middleware for SuperInstance fleet Cloudflare Workers
We found that fleet-auth demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Company News
Socket’s first CISO brings deep experience securing high-growth SaaS companies as open source supply chain threats accelerate.

Company News
Replit is integrating Socket Firewall into its AI-powered development experience to help protect builders from malicious open source packages.

Security News
npm confirmed a tooling bug incorrectly marked several one-character packages as security holders and said it was working on a rollback.