ATAuth - DigitalOcean Kubernetes Handoff #
This document provides everything you need to connect to and manage the ATAuth deployment on DigitalOcean managed Kubernetes.
Cluster Overview #
| Item | Value |
|---|---|
| Provider | DigitalOcean Managed Kubernetes |
| Cluster | do-nyc1-storm-dr-cluster (nyc1 region) |
| Kubernetes | v1.34.1 |
| Namespace | atauth |
| Nodes | 3x worker (4 vCPU, 8GB RAM each) |
| Container registry | registry.digitalocean.com/ghostmesh-registry |
Connecting with kubectl #
You will receive a kubeconfig file (atauth-team-kubeconfig.yaml) separately. This kubeconfig is scoped to the atauth namespace only -- you cannot access other namespaces on the cluster.
Setup #
# Option 1: Set KUBECONFIG env var
export KUBECONFIG=/path/to/atauth-team-kubeconfig.yaml
# Option 2: Merge into your default config
cp ~/.kube/config ~/.kube/config.backup
KUBECONFIG=~/.kube/config:/path/to/atauth-team-kubeconfig.yaml kubectl config view --merge --flatten > /tmp/merged && mv /tmp/merged ~/.kube/config
kubectl config use-context atauth-team@do-nyc1-storm-dr-cluster
Verify #
kubectl get pods -n atauth
# Should show the atauth pod running
kubectl get deployments -n atauth
# NAME READY UP-TO-DATE AVAILABLE
# atauth 1/1 1 1
What You Have Access To #
Your ServiceAccount (atauth-team) has full CRUD access within the atauth namespace:
- Pods (including logs, exec, port-forward)
- Deployments and ReplicaSets
- Services
- Ingresses
- ConfigMaps
- Secrets
- PersistentVolumeClaims
- Events
You do not have access to cluster-scoped resources or other namespaces.
Current Deployment Architecture #
┌──────────────────────┐
Internet ──> nginx │ Ingress (nginx) │
ingress ──┤ apricot.workingtitle │
│ .zip │
└──────────┬───────────┘
│
┌──────────▼───────────┐
│ Service (ClusterIP) │
│ atauth:3100 │
└──────────┬───────────┘
│
┌──────────▼───────────┐
│ Deployment │
│ 1 replica, Recreate │
│ strategy │
└──────────┬───────────┘
│
┌──────────▼───────────┐
│ PVC (1Gi, RWO) │
│ do-block-storage │
│ SQLite DB at │
│ /app/data/gateway.db │
└──────────────────────┘
Key Resources #
| Resource | Name | Notes |
|---|---|---|
| Deployment | atauth |
1 replica, Recreate strategy (required -- RWO PVC) |
| Service | atauth |
ClusterIP, port 3100 |
| Ingress | atauth |
Host: apricot.workingtitle.zip, TLS via atauth-tls secret |
| PVC | atauth-data |
1Gi, do-block-storage, holds SQLite DB |
| ConfigMap | atauth-config |
Non-sensitive env vars (CORS, OIDC issuer, WebAuthn, etc.) |
| Secret | atauth-secrets |
ATAUTH_ADMIN_TOKEN, ATAUTH_OIDC_KEY_SECRET, ATAUTH_FORWARD_AUTH_SESSION_SECRET |
| Secret | atauth-tls |
TLS cert/key for the ingress |
| Secret | registry-ghostmesh-registry |
Docker registry pull credentials |
Container Image #
- Registry:
registry.digitalocean.com/ghostmesh-registry/atauth - Tags: short git SHA (e.g.,
43d2471) +latest - Platform:
linux/amd64(required -- DO nodes are amd64) - Dockerfile:
gateway/Dockerfile
Environment Variables #
Sourced from atauth-config ConfigMap:
| Env Var | ConfigMap Key | Description |
|---|---|---|
OAUTH_CLIENT_ID |
ATAUTH_OAUTH_CLIENT_ID |
AT Protocol OAuth client metadata URL |
OAUTH_REDIRECT_URI |
ATAUTH_OAUTH_REDIRECT_URI |
OAuth callback URL |
CORS_ORIGINS |
ATAUTH_CORS_ORIGINS |
Comma-separated allowed origins |
OIDC_ENABLED |
ATAUTH_OIDC_ENABLED |
Enable OIDC provider |
OIDC_ISSUER |
ATAUTH_OIDC_ISSUER |
OIDC issuer URL |
FORWARD_AUTH_ENABLED |
ATAUTH_FORWARD_AUTH_ENABLED |
Enable nginx forward-auth proxy |
FORWARD_AUTH_SESSION_TTL |
ATAUTH_FORWARD_AUTH_SESSION_TTL |
Session TTL in seconds |
FORWARD_AUTH_PROXY_COOKIE_TTL |
ATAUTH_FORWARD_AUTH_PROXY_COOKIE_TTL |
Proxy cookie TTL in seconds |
MFA_ENABLED |
ATAUTH_MFA_ENABLED |
Enable passkey/WebAuthn MFA |
WEBAUTHN_RP_ID |
ATAUTH_WEBAUTHN_RP_ID |
WebAuthn relying party ID |
WEBAUTHN_ORIGIN |
ATAUTH_WEBAUTHN_ORIGIN |
WebAuthn origin URL |
WEBAUTHN_RP_NAME |
ATAUTH_WEBAUTHN_RP_NAME |
WebAuthn relying party display name |
Sourced from atauth-secrets Secret:
| Env Var | Secret Key | Description |
|---|---|---|
ADMIN_TOKEN |
ATAUTH_ADMIN_TOKEN |
Admin API bearer token |
OIDC_KEY_SECRET |
ATAUTH_OIDC_KEY_SECRET |
OIDC signing key secret |
FORWARD_AUTH_SESSION_SECRET |
ATAUTH_FORWARD_AUTH_SESSION_SECRET |
Session encryption secret |
Hardcoded in deployment spec:
| Env Var | Value |
|---|---|
PORT |
3100 |
HOST |
0.0.0.0 |
NODE_ENV |
production |
DB_PATH |
/app/data/gateway.db |
Common Operations #
View logs #
kubectl logs -n atauth deployment/atauth -f
Restart the deployment #
kubectl rollout restart deployment/atauth -n atauth
kubectl rollout status deployment/atauth -n atauth
Deploy a new image #
kubectl set image deployment/atauth atauth=registry.digitalocean.com/ghostmesh-registry/atauth:<tag> -n atauth
kubectl rollout status deployment/atauth -n atauth --timeout=120s
Update config #
# Edit a ConfigMap value
kubectl patch configmap atauth-config -n atauth --type merge \
-p '{"data":{"ATAUTH_CORS_ORIGINS":"https://apricot.workingtitle.zip,https://new-app.example.com"}}'
# Restart to pick up changes (env vars are read at startup)
kubectl rollout restart deployment/atauth -n atauth
Update secrets #
# Update a secret value (must be base64 encoded)
kubectl patch secret atauth-secrets -n atauth --type merge \
-p '{"data":{"ATAUTH_ADMIN_TOKEN":"'$(echo -n "new-token-value" | base64)'"}}'
kubectl rollout restart deployment/atauth -n atauth
Port-forward for local access #
kubectl port-forward -n atauth svc/atauth 3100:3100
# ATAuth is now accessible at http://localhost:3100
Shell into the container #
kubectl exec -it -n atauth deployment/atauth -- /bin/sh
Check health #
# Via port-forward
curl http://localhost:3100/health
# Or check readiness probe status
kubectl describe pod -n atauth -l app=atauth | grep -A5 "Conditions:"
CI/CD Pipeline #
The existing Gitea Actions workflow (.gitea/workflows/deploy.yml) runs on push to main:
- Test job (
ubuntu-latestrunner):npm ci, typecheck, lint, vitest - Build and deploy job (
hostrunner): Docker build, push to DO registry,kubectl set image, rollout status
The pipeline uses these Gitea org-level secrets:
DO_REGISTRY_TOKEN-- DO container registry API tokenDO_KUBECONFIG-- base64-encoded kubeconfig for theci-deployerServiceAccount
Important Gotchas #
-
Recreate strategy is required. The PVC is RWO (ReadWriteOnce). RollingUpdate will deadlock if the new pod lands on a different node than the old one.
-
Domain must be
workingtitle.zip, notarcnode.xyz. Thearcnode.xyzdomain hosts a PDS (AT Protocol Personal Data Server). Using it for ATAuth causes same-site cookie/header conflicts. -
Docker images must target
linux/amd64. The DO worker nodes run amd64. ARM images will crash with exec format errors. -
HMAC token encoding. ATAuth signs HMAC tokens with
createHmac('sha256', secretString)using the UTF-8 encoding of the hex secret. Downstream services verifying tokens must match this encoding. -
req.accepts('json')matches*/*. Usereq.is('json')for Content-Type checks in Express routes. -
Registry pull secret must exist in namespace. The
registry-ghostmesh-registrysecret is already present. If you recreate the namespace, you must re-add it or image pulls will fail. -
SQLite WAL mode. The DB runs in WAL mode on DO block storage. Single-writer only -- do not scale beyond 1 replica.
Live URLs #
| URL | Description |
|---|---|
https://apricot.workingtitle.zip |
ATAuth gateway (public) |
https://apricot.workingtitle.zip/.well-known/openid-configuration |
OIDC discovery |
https://apricot.workingtitle.zip/admin/login |
Admin dashboard |
https://apricot.workingtitle.zip/health |
Health check endpoint |