Securing Beacon Services
Watchlight Beacon uses a three-tier security model for inter-service communication. Choose the tier that matches your deployment environment.
Security Tiers
| Tier | Auth Method | Use Case |
|---|---|---|
| Tier 1: Development | None (anonymous) | Local dev, demos, POC |
| Tier 2: API Keys | bcrypt-hashed keys via env vars or K8s secrets | Docker Compose, staging, small teams |
| Tier 3: Enterprise | OIDC M2M, mTLS, or SPIFFE | Production, regulated environments |
API keys, hashes, and credentials must never be stored in files on disk. Use environment variables, K8s secrets, or a vault (OpenBao/HashiCorp Vault) for all secret material.
Tier 1: Development (Default)
No configuration needed. All services communicate without credentials on the Docker network.
# docker-compose.yml
wl-apdp:
environment:
AUTH_REQUIRE_AUTH: "false"
AUTH_ALLOW_ANONYMOUS: "true"
This is the default when you run docker compose up. Suitable for local development and demos only.
Tier 2: API Keys
Each service authenticates with a bcrypt-hashed API key. Keys are persisted in the APDP database (encrypted at rest via bcrypt one-way hashing) and survive restarts automatically.
Step 1: Generate Keys via the Admin UI
Open the APDP dashboard → Settings → Service API Keys → Generate Key.
Enter a name (e.g., wl-proxy), select roles, and click Generate. The UI shows:
- Raw API Key — copy this immediately and give it to the service via env var or K8s secret
- The raw key is shown once and never stored anywhere
Keys are persisted in the APDP database automatically. No manual hash management needed. For environments without a database (in-memory mode), use the AUTH_API_KEYS env var.
Key Rotation
To rotate a service key, click the Rotate button (↻) next to the key in the admin UI. This:
- Revokes the current key immediately
- Generates a new key with the same name and roles
- Shows the new raw key (copy it immediately)
Update the service's env var with the new key. The old key will be rejected on the next request.
Step 2 (Optional): Bootstrap Keys via Environment Variable
For environments without a database, or to bootstrap initial keys before the admin UI is accessible, use the AUTH_API_KEYS env var:
# docker-compose.yml
wl-apdp:
environment:
AUTH_REQUIRE_AUTH: "true"
AUTH_ALLOW_ANONYMOUS: "false"
AUTH_API_KEYS: |
[
{
"name": "wl-proxy",
"key_hash": "$2b$10$...",
"principal_id": "wl-proxy",
"principal_type": "Service",
"roles": ["proxy"]
},
{
"name": "wl-secrets-broker",
"key_hash": "$2b$10$...",
"principal_id": "wl-secrets-broker",
"principal_type": "Service",
"roles": ["wsb"]
},
{
"name": "adk-plugin",
"key_hash": "$2b$10$...",
"principal_id": "plugin-adk",
"principal_type": "Service",
"roles": ["plugin"]
}
]
For Kubernetes: Use K8s Secrets
apiVersion: v1
kind: Secret
metadata:
name: beacon-auth-keys
type: Opaque
stringData:
AUTH_API_KEYS: |
[{"name":"wl-proxy","key_hash":"$2b$10$...","principal_id":"wl-proxy","principal_type":"Service","roles":["proxy"]}]
PROXY_APDP_API_KEY: "raw-proxy-key"
WSB_APDP_API_KEY: "raw-wsb-key"
Reference in your deployment:
envFrom:
- secretRef:
name: beacon-auth-keys
Step 3: Configure Services with Raw Keys
Pass the raw API key to each service via environment variable:
wl-proxy:
environment:
APDP_API_KEY: "raw-proxy-key" # From K8s secret or vault
WSB_API_KEY: "raw-wsb-key" # From K8s secret or vault
wl-secrets-broker:
environment:
WSB_APDP_API_KEY: "raw-wsb-key" # From K8s secret or vault
For plugins:
from watchlight_adk import WatchlightPlugin
plugin = WatchlightPlugin(
apdp_url="http://wl-apdp:8081",
apdp_api_key=os.environ["WATCHLIGHT_API_KEY"],
)
Step 4: Verify
# Should succeed (with valid key)
curl -X POST http://localhost:8081/authorize \
-H "Authorization: Bearer raw-proxy-key" \
-H "Content-Type: application/json" \
-d '{"principal":"Agent::\"test\"","action":"Action::\"read\"","resource":"Tool::\"test\"","context":{}}'
# Should fail (no key)
curl -X POST http://localhost:8081/authorize \
-H "Content-Type: application/json" \
-d '{"principal":"Agent::\"test\"","action":"Action::\"read\"","resource":"Tool::\"test\"","context":{}}'
# → 401 Unauthorized
Role-Based Endpoint Access
When auth is enabled, each service role has scoped access:
| Endpoint | proxy | wsb | plugin | admin |
|---|---|---|---|---|
POST /authorize | Yes | Yes | Yes | Yes |
POST /v1/events | Yes | No | Yes | Yes |
GET /agents/:id | No | Yes | No | Yes |
GET/POST /policies | No | No | No | Yes |
POST /agents | No | No | No | Yes |
Tier 3: Enterprise (Planned)
OIDC Machine-to-Machine
Each service registers as an OAuth2 client in your identity provider (Okta, Auth0, Azure AD). Services obtain JWTs via client credentials flow. APDP validates tokens against the IdP's JWKS endpoint.
- Zero shared secrets between services and APDP
- Token auto-refresh before expiry
- Centralized service identity management in your IdP
mTLS (Mutual TLS)
All inter-service HTTP connections authenticated via client certificates issued by an internal CA.
- Every connection mutually authenticated at the TLS layer
- Certificate rotation via standard PKI tooling
- No application-level credentials needed
SPIFFE/SPIRE (Kubernetes)
For K8s deployments, SPIRE sidecars auto-issue JWT-SVIDs to each service with 5-minute rotation.
- Zero shared secrets
- Auto-rotating workload identity
- Trust domain-based access control
Communication Topology
┌──────────┐ Bearer ┌──────────────┐ NATS ┌───────────────┐
│ WL-Proxy │──────────▶│ WL-APDP │─────────▶│ NATS │
│ │ auth │ │ (events) │ JetStream │
└──────┬───┘ └──────────────┘ └───────┬───────┘
│ Bearer │
│ auth ▼
▼ ┌───────────────┐
┌──────────────┐ Bearer ┌──────────────┐ │ WL-Execution │
│ WL-Secrets │─────────▶│ WL-APDP │ │ Graph │
│ Broker │ auth │ │ └───────────────┘
└──────────────┘ └──────────────┘
Plugins:
├─ POST /authorize → WL-APDP (Bearer auth)
└─ POST /v1/events → WL-APDP (Bearer auth)
Environment Variables Reference
| Service | Variable | Purpose |
|---|---|---|
| WL-APDP | AUTH_REQUIRE_AUTH | Enable authentication (default: true) |
| WL-APDP | AUTH_ALLOW_ANONYMOUS | Allow unauthenticated requests (default: false) |
| WL-APDP | AUTH_API_KEYS | JSON array of bcrypt-hashed service key providers |
| WL-Proxy | APDP_API_KEY | Raw API key for APDP calls |
| WL-Proxy | WSB_API_KEY | Raw API key for WSB calls |
| WL-Secrets-Broker | WSB_APDP_API_KEY | Raw API key for APDP calls |
| Plugin | apdp_api_key param | Raw API key for APDP calls |