- Documents recent infrastructure cleanup (8 CTs destroyed, 35 DNS records removed, Headscale cleanup) - Adds 24 new runbooks covering Authentik, PeerTube, Meshtastic, RECON, Proxmox, Mailcow, Internet Archive, GPU routing - Adds project documentation for headscale, vaultwarden, peertube, matrix, mmud, advbbs, arr stack - Updates services.md, environment.md, caddy.md, authentik.md to match live infrastructure - Removes 4 deprecated runbook duplicates (canonical versions live in projects/) - Adds .gitignore for binary archives and editor temp files Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
14 KiB
Authentik SSO Configuration
Location
- Server: Contabo (5.189.158.149 / 100.64.0.1)
- URL: https://auth.echo6.co
- Internal Port: 9000
API Access
API token stored in /home/zvx/projects/.ref/credentials as AUTHENTIK_API_TOKEN
# Test API access
curl -s "https://auth.echo6.co/api/v3/core/applications/" \
-H "Authorization: Bearer $AUTHENTIK_API_TOKEN" | python3 -m json.tool
Flow UUIDs
Required for OAuth2 provider creation:
| Flow | UUID |
|---|---|
| Authorization (implicit) | 86051292-389f-4bd9-b0f9-53cd32f197fd |
| Authorization (explicit) | 6f9f5c89-9f98-4776-9e0d-a72a8ad17963 |
| Invalidation | ed861c0d-2c81-4c3d-819b-946a21c4296a |
| Provider Invalidation | 1eb91626-19a3-4f45-b384-d699c6189197 |
Signing Key
| Key | UUID |
|---|---|
| authentik Self-signed Certificate | 09f508f0-6b8e-4031-8563-0d3cebf86868 |
Property Mappings (Standard OIDC Set)
Include these three in every provider:
| Mapping | UUID |
|---|---|
| OpenID Connect scope: openid | c6426dad-0d85-4daa-89fe-cb850ad4bfd7 |
| Echo6 OAuth: email (verified=true) | 02c22323-da89-457a-bc12-7f4dd6a3d8ab |
| OpenID Connect scope: profile | 113ab791-fa04-4e8c-b103-09d706bc21b4 |
Note: The default email scope (096b0d6f) was changed in 2025.10 to return email_verified: false. Our custom scope (02c22323) overrides this to always return true. All 14 OAuth2 providers were migrated to use the custom scope on 2026-02-16.
Current OAuth2 Providers
| PK | Name | Client ID | Application | Redirect URI |
|---|---|---|---|---|
| 1 | Mailcow | mailcow | Mailcow | https://mail.echo6.co/sso/oidc |
| 2 | Forgejo | forgejo | Forgejo | https://forge.echo6.co/user/oauth2/Authentik/callback |
| 3 | Vaultwarden | vaultwarden | Vaultwarden | https://vault.echo6.co/identity/connect/callback |
| 4 | Proxmox | proxmox | Proxmox VE | https://proxmox.echo6.co (regex) |
| 5 | Headscale | headscale | Headscale VPN | https://vpn.echo6.co/oidc/callback |
| 6 | Headplane | headplane | Headplane | https://vpn.echo6.co/admin/oidc/callback |
| 8 | Nextcloud | nextcloud | Nextcloud | https://nextcloud.echo6.co/apps/oidc_login/oidc |
| 9 | Immich | immich | Immich | https://immich.echo6.co/auth/login, /api/oauth/mobile-redirect |
| 10 | jellyfin | jellyfin | Jellyfin | https://jellyfin.echo6.co/sso/OID/redirect/Authentik |
| 11 | jellyseer | jellyseer | Jellyseer | https://requests.echo6.co/(login|api/v1/auth/oidc-callback).* (regex) |
| 12 | PeerTube | peertube | PeerTube | https://stream.echo6.co/plugins/auth-openid-connect/... |
| 13 | WATCHTOWER | watchtower | WATCHTOWER | Forward auth (proxy provider) |
| 14 | Open WebUI | open-webui | Open WebUI | https://ai.echo6.co/oauth/oidc/callback |
| 15 | Matrix | 93kCoZkBlnJyD9EcAm7E4btKflecOcBm9DGONB5T | Matrix | https://matrix.echo6.co/_synapse/client/oidc/callback |
| 16 | Files Forward Auth | — | Files | Forward auth (proxy provider) |
| 17 | LiveSync Provisioner | ZBoLdYmxlSUyMqgekswIPS4YuaeBn5uCr8GtWm5H | LiveSync | Forward auth (proxy provider) |
Groups
| Name | PK | Superuser | Members | Used By |
|------|----|-----------|---------|----- ---|
| authentik Admins | 9944e153-f860-4443-81d1-ae544f611806 | Yes | akadmin, matt | All apps (admin access) |
| media-users | 0820b2b8-6c54-4c20-9a0a-872820e6d9ea | No | jodie, matt | Jellyfin, Jellyseer, PeerTube |
| ai-users | 0631b273-cfd8-4ed1-afa6-e262d0dc5a69 | No | matt | Open WebUI |
| cloud-users | db3cbf5d-8057-4e33-8e8d-95bfdb35fbac | No | — | Immich, Nextcloud |
| communication-users | 31bce176-cd86-4aea-8db3-a57e03d5c2d1 | No | — | Mailcow, Matrix |
| productivity-users | 698d80c7-7c29-43cd-b5d4-9eb24c85a6cc | No | — | — |
| security-users | f345a043-c2a4-4906-a43b-9860eae86ee1 | No | — | — |
| proxmox_admins | d85a868d-7d1e-4585-92a8-b8bb86771b53 | No | akadmin, matt | Proxmox VE |
| proxmox_users | cf26703a-a824-47dd-9550-30b848a8ce5f | No | — | Proxmox VE |
| authentik Read-only | ce03664b-46f3-43b7-9967-5f65d591fdb6 | No | — | RBAC read-only role |
| livesync-users | 8e575a86-326e-4df8-8828-8379d8ab861f | No | matt | LiveSync |
Application Access Pattern
Every application uses policy_engine_mode: "any" with two group bindings:
- authentik Admins — gives admin/superuser access to all apps
- Service-specific group — controls which regular users can access the app
Users must be in at least one bound group to access the application.
Create New API Token
ssh root@100.64.0.1 'docker exec -i authentik-server ak shell' <<'PYEOF'
from authentik.core.models import Token, TokenIntents, User
user = User.objects.get(username="akadmin")
Token.objects.filter(identifier="token-name").delete()
t = Token(identifier="token-name", user=user, intent=TokenIntents.INTENT_API, expiring=False, managed=None)
t.save()
print(t.key)
PYEOF
Full OAuth2 App Setup (Provider + Application + Group + Bindings)
Step 1: Create the OAuth2 provider
curl -s -X POST "https://auth.echo6.co/api/v3/providers/oauth2/" \
-H "Authorization: Bearer $AUTHENTIK_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "AppName",
"authorization_flow": "86051292-389f-4bd9-b0f9-53cd32f197fd",
"invalidation_flow": "ed861c0d-2c81-4c3d-819b-946a21c4296a",
"property_mappings": [
"c6426dad-0d85-4daa-89fe-cb850ad4bfd7",
"02c22323-da89-457a-bc12-7f4dd6a3d8ab",
"113ab791-fa04-4e8c-b103-09d706bc21b4"
],
"client_type": "confidential",
"client_id": "appname",
"client_secret": "<generate-a-long-random-secret>",
"redirect_uris": [{"matching_mode": "strict", "url": "https://app.echo6.co/callback"}],
"sub_mode": "user_username",
"signing_key": "09f508f0-6b8e-4031-8563-0d3cebf86868",
"include_claims_in_id_token": true
}'
# Note the "pk" from the response — needed for Step 2
Step 2: Create the application
curl -s -X POST "https://auth.echo6.co/api/v3/core/applications/" \
-H "Authorization: Bearer $AUTHENTIK_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "AppName",
"slug": "appname",
"provider": PROVIDER_PK,
"meta_launch_url": "https://app.echo6.co",
"policy_engine_mode": "any"
}'
# Note the "pk" from the response — needed for Step 4
Step 3: Create the user group
curl -s -X POST "https://auth.echo6.co/api/v3/core/groups/" \
-H "Authorization: Bearer $AUTHENTIK_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "appname-users",
"is_superuser": false,
"users": [7]
}'
# user 7 = matt. Note the "pk" from the response.
Step 4: Bind groups to the application
# Bind authentik Admins
curl -s -X POST "https://auth.echo6.co/api/v3/policies/bindings/" \
-H "Authorization: Bearer $AUTHENTIK_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"target": "APPLICATION_PK",
"group": "9944e153-f860-4443-81d1-ae544f611806",
"order": 0, "enabled": true, "negate": false, "timeout": 30
}'
# Bind service-specific group
curl -s -X POST "https://auth.echo6.co/api/v3/policies/bindings/" \
-H "Authorization: Bearer $AUTHENTIK_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"target": "APPLICATION_PK",
"group": "GROUP_PK",
"order": 0, "enabled": true, "negate": false, "timeout": 30
}'
Step 5: Verify
# OIDC discovery should return endpoints
curl -s "https://auth.echo6.co/application/o/appname/.well-known/openid-configuration" | python3 -m json.tool
# Check bindings
curl -s "https://auth.echo6.co/api/v3/policies/bindings/?target=APPLICATION_PK" \
-H "Authorization: Bearer $AUTHENTIK_API_TOKEN"
Common Redirect URI Patterns
| Application Type | Redirect URI Pattern |
|---|---|
| Web app (generic) | https://app.echo6.co/callback |
| Web app (oauth path) | https://app.echo6.co/oauth/callback |
| Open WebUI (OIDC) | https://app.echo6.co/oauth/oidc/callback |
| Forgejo | https://app.echo6.co/user/oauth2/Authentik/callback |
| Jellyfin (SSO plugin) | https://app.echo6.co/sso/OID/redirect/Authentik |
| Caddy forward auth | https://app.echo6.co/outpost.goauthentik.io/callback |
Users
| PK | Username | Name | |
|---|---|---|---|
| 6 | akadmin | authentik Default Admin | root@example.com |
| 7 | matt | Matt Johnson | matt@echo6.co |
| 9 | jodie | Jodie | johnsonsinidaho@gmail.com |
Email Invitation System
SMTP Configuration
Authentik sends email via Mailcow:
| Setting | Value |
|---|---|
| SMTP Host | mail.echo6.co |
| SMTP Port | 587 (STARTTLS) |
| Username | no-reply@echo6.co |
| From | no-reply@echo6.co |
Important: The no-reply@echo6.co mailbox MUST have authsource=mailcow in the Mailcow database (not generic-oidc). If it gets reset to generic-oidc, SMTP auth will fail because Dovecot tries to authenticate via Authentik SSO instead of the local password. Fix with:
docker exec mailcowdockerized-mysql-mailcow-1 mysql -u mailcow -p<DBPASS> mailcow \
-e "UPDATE mailbox SET authsource='mailcow' WHERE username='no-reply@echo6.co'"
Enrollment Flow
| Component | PK |
|---|---|
| Flow (invitation-enrollment) | 184a9e20-f5c5-4b44-8775-266a568439c0 |
| Invitation stage (enrollment-invitation) | 994252a6-a659-4304-b9aa-a6591857a53b |
| Prompt stage (enrollment-credentials) | a5e2a2f1-95c9-4627-a3dd-e0915ff64cda |
| User write stage (enrollment-user-write) | 05f565e0-9d64-4ecc-b738-8ffc697ad829 |
| User login stage (enrollment-user-login) | f23958ea-47bd-4fb2-a657-b6f85fc6331a |
Stage order: Invitation (10) → Prompt (20) → User Write (30) → User Login (40)
Flow settings: authentication=require_unauthenticated, continue_flow_without_invitation=false
User write creates users under users/enrolled path.
Email Automation
Uses the community pattern from authentik/discussions/13305:
| Component | PK |
|---|---|
| Expression policy (invitation-email-sender) | 770188de-2b60-4e87-820a-fa64d419ed89 |
| Notification rule (invitation-email-trigger) | a145a8c9-b3eb-440d-8b77-27139c346a17 |
When an invitation is created with an email field in custom attributes, the expression policy triggers ak_send_email() to send the enrollment link to the invitee automatically.
How to Use
Invite via email (Admin UI → Directory → Invitations → Create):
- Name it, select Invitation Enrollment flow, toggle Single use on, set expiry
- Custom attributes:
name: Jane Smith email: jane@example.com - Click Create — email is sent automatically
Invite via link (no email):
- Same as above but leave custom attributes empty (or omit
emailfield) - Click Create → expand the row → copy the invitation link
- Send the link manually
Branding & Theming
Echo6 cyberpunk branding applied to Authentik 2025.12.4 via System → Brands.
Brand Settings
| Setting | Value |
|---|---|
| Brand title | echo6 |
| Theme | dark (forced, not automatic) |
| Logo | /media/custom/echo6-logo.png (uploaded via Customization → Files) |
| Favicon | /media/custom/echo6-favicon.png (uploaded via Customization → Files) |
| Custom CSS | Echo6 Authentik CSS (~200 rules, applied via Brand → Custom CSS field) |
Flow Titles
| Flow | Title |
|---|---|
| Authentication | echo6 // login |
| Invalidation | echo6 // logout |
| Recovery | echo6 // recovery |
| User Settings | echo6 // settings |
Custom CSS Highlights
- Font: JetBrains Mono globally (with
:not()exclusions for FontAwesome/PatternFly icon fonts) - Colors: Cyan
#28C0E8primary accent, Yellow#F0D848secondary, dark backgrounds#0a0e17/#111827/#1a2332 - Login card: Dark background, cyan-glow focus on inputs, branded submit button
- Admin sidebar: Dark with cyan hover/active states
- User dashboard: 3-column grid layout, dark cards with cyan border on hover
- Application icons: Custom SVG icons uploaded for all 15 services
CSS Storage
The custom CSS is stored in the Brand model's branding_custom_css field. To update:
# Copy CSS to Contabo
scp /path/to/echo6-authentik.css root@100.64.0.1:/opt/authentik/branding/custom.css
# Load into Brand model via ak shell
ssh root@100.64.0.1 'docker exec -i authentik-server ak shell' <<'PYEOF'
from authentik.brands.models import Brand
b = Brand.objects.get(domain="auth.echo6.co")
b.branding_custom_css = open("/media/custom/custom.css").read()
b.save()
PYEOF
# Restart to apply
ssh root@100.64.0.1 'cd /opt/authentik && docker compose restart server worker'
SSO Launch URL Pattern
All authenticated service links use Authentik's application launch URL:
https://auth.echo6.co/application/launch/<app-slug>/
This provides seamless SSO: authenticated users pass through to the app, unauthenticated users get the login page then redirect to the app. Used by the SearXNG waffle menu and nav bar.
| App Slug | Service | Launch URL |
|---|---|---|
| open-webui | Aurora (AI) | https://auth.echo6.co/application/launch/open-webui/ |
| peertube | Stream | https://auth.echo6.co/application/launch/peertube/ |
| files | Files | https://auth.echo6.co/application/launch/files/ |
| watchtower | Watchtower | https://auth.echo6.co/application/launch/watchtower/ |
| immich | Photos | https://auth.echo6.co/application/launch/immich/ |
| mailcow | https://auth.echo6.co/application/launch/mailcow/ |
|
| nextcloud | Cloud | https://auth.echo6.co/application/launch/nextcloud/ |
| jellyfin | Jellyfin | https://auth.echo6.co/application/launch/jellyfin/ |
| jellyseer | Requests | https://auth.echo6.co/application/launch/jellyseer/ |
Brand Color Reference
| Color | Hex | Usage |
|---|---|---|
| Cyan | #28C0E8 |
Primary accent, links, focus states |
| Cyan Light | #5DD4F5 |
Hover states |
| Yellow | #F0D848 |
Secondary accent, badges |
| BG Primary | #0a0e17 |
Page backgrounds |
| BG Secondary | #111827 |
Cards, inputs |
| BG Tertiary | #1a2332 |
Elevated surfaces |
| Border | #1e3a5f |
All borders |
| Text Primary | #e0e6ed |
Main text |
| Text Muted | #7a8ca0 |
Secondary text |
Last updated: 2026-02-18 — Added LiveSync proxy provider (PK 17) + livesync-users group, Files Forward Auth (PK 16). Authentik 2025.12.4