Skip to main content

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

TierAuth MethodUse Case
Tier 1: DevelopmentNone (anonymous)Local dev, demos, POC
Tier 2: API Keysbcrypt-hashed keys via env vars or K8s secretsDocker Compose, staging, small teams
Tier 3: EnterpriseOIDC M2M, mTLS, or SPIFFEProduction, regulated environments
Secrets Never on Disk

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 → SettingsService API KeysGenerate 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
tip

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:

  1. Revokes the current key immediately
  2. Generates a new key with the same name and roles
  3. 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:

Endpointproxywsbpluginadmin
POST /authorizeYesYesYesYes
POST /v1/eventsYesNoYesYes
GET /agents/:idNoYesNoYes
GET/POST /policiesNoNoNoYes
POST /agentsNoNoNoYes

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

ServiceVariablePurpose
WL-APDPAUTH_REQUIRE_AUTHEnable authentication (default: true)
WL-APDPAUTH_ALLOW_ANONYMOUSAllow unauthenticated requests (default: false)
WL-APDPAUTH_API_KEYSJSON array of bcrypt-hashed service key providers
WL-ProxyAPDP_API_KEYRaw API key for APDP calls
WL-ProxyWSB_API_KEYRaw API key for WSB calls
WL-Secrets-BrokerWSB_APDP_API_KEYRaw API key for APDP calls
Pluginapdp_api_key paramRaw API key for APDP calls