Skip to main content

OpenBao Secret Structure

The portal uses OpenBao (Vault-compatible) as a secrets backend.

Connection Details​

SettingValue
Address (in-cluster)http://openbao.openbao.svc.cluster.local:8200
Token secretdev-portal-secrets → VAULT_TOKEN in namespace dev-portal
Secrets engineKV v2
Mount pathai-factory

Per-User Secret Path​

ai-factory/data/apisix/<username>
├── key: "<32-char base64url API key>"
├── allowed_models: ["route-a", "route-b"]
├── email: "jane.doe@example.org" (these three: present once the owner has
├── name: "Jane Doe" logged in or generated a key since they
└── sub: "<Authentik user id>" were introduced)
FieldDescription
keyThe user's current API key
allowed_modelsRoute names the user has been granted access to
email, name, subWho the drawer belongs to. Authentik is authoritative for these; the copy here comes from the OIDC userinfo when a key is generated and is refreshed on every OIDC login, best effort, so it can be stale. An empty claim never overwrites a stored value. A login never creates a drawer, so a user who has not generated a key has nothing here, and drawers created before these fields existed get them on the owner's next login (there is no backfill job). Once consumer names are Authentik's short usernames, these are how you find and contact the person behind one.

Manual Operations​

In exceptional circumstances you can update secrets directly using the bao CLI:

# Read a user's secret
bao kv get ai-factory/apisix/<username>

# Grant model access manually
bao kv patch ai-factory/apisix/<username> allowed_models='["route-a","route-b"]'

# Delete a user's secret permanently (all versions; the portal does the same on revoke)
bao kv metadata delete ai-factory/apisix/<username>
warning

Direct edits to OpenBao bypass the portal's route whitelist sync. Always prefer the Admin panel to keep APISIX and OpenBao in sync.

Renaming a User​

The drawer label, the ExternalSecret, the Kubernetes Secret and the ApisixConsumer are all named after the username Authentik sends. If that username changes (for example when Authentik switches from email addresses to short usernames), nothing renames itself: the old objects survive with a still-valid key and the user's next key generation creates a second set. Do the rename together with the change in Authentik, not afterwards. By hand:

  1. Copy the drawer to the new label (bao kv get the old one, bao kv put the new one with the same fields), then bao kv metadata delete the old label.
  2. Create the ExternalSecret and ApisixConsumer under the new name the same way the portal does (ProvisionResources in k8s.go: the ExternalSecret's remoteRef.key is apisix/<new label>), then delete the old pair.
  3. Update any hand-written consumer or secret names in the infra repo (KFP door and MLflow bridge lists in values-dev.yaml).

sub is Authentik's own id for the user and survives a username or email change, so it is how you confirm that an old drawer and a new one belong to the same person. It is per Authentik instance: dev and prod give the same person different values.

Rotating the OpenBao Token​

  1. Generate a new token in OpenBao with the required policy
  2. Update the Kubernetes secret:
    kubectl patch secret dev-portal-secrets -n dev-portal \
    --type='json' \
    -p='[{"op":"replace","path":"/data/VAULT_TOKEN","value":"'$(echo -n "<new-token>" | base64)'"}]'
  3. Restart the portal API:
    kubectl rollout restart deployment/dev-portal-api -n dev-portal

Required OpenBao Policy​

path "ai-factory/data/apisix/*" {
capabilities = ["create", "read", "update", "delete", "list"]
}

path "ai-factory/metadata/apisix/*" {
capabilities = ["list", "delete"]
}