Files
KNELAgentIdentityProvisioning/skills/agent-provisioning/SKILL.md
T
TSYS Group COO bde7845490 skills: agent-provisioning - codified identity stand-up workflow
Policy rulings (#942): unique passwords, Cloudron-only SSO accounts,
no reachableceo credentials, TOTP seeds MUST be captured to the vault.
Cloudron admin API wrapper, cookie-jar OIDC login, RFC-6238-tested
TOTP helper, per-system SSO/API references, incident log.
2026-09-08 04:07:14 -05:00

7.8 KiB

name, description
name description
agent-provisioning Provision TSYS Group agent identities end-to-end: Cloudron account (unique password, TOTP with seed captured to the TSGCOO vault), SSO first-login into Redmine / Gitea / Discourse / GLPI, per-system API key minting, vault item schema, and verification. Use whenever asked to "stand up" an identity or agent, create/fix Cloudron users, wire SSO, debug identity logins or 2FA, or store identity credentials — even if the word "provisioning" is not used. Policy-critical: encodes founder rulings #942 (unique passwords, Cloudron-only accounts, no reachableceo credentials).

Agent identity provisioning (TSYS Group)

Stand up an agent identity so it can operate its own accounts in Cloudron and the four SSO-wired systems, with every credential landing in the TSGCOO Bitwarden vault. The Cloudron is the ONLY identity source: never create local accounts in Redmine, Gitea, Discourse, or GLPI — the system account comes into existence when the identity completes its first SSO login there.

Non-negotiable policy (founder rulings — violating these invalidates the work)

  1. Unique password per identity. One identity, one password, used only for its Cloudron account. Shared passwords are banned (Redmine #942).
  2. Cloudron-only accounts. System access = SSO first-login. No local accounts, no admin-created users in the downstream systems.
  3. Never use reachableceo credentials. Items like creds/gitea-admin, creds/gitea, creds/redmine are the founder's personal accounts and carry RESTRICTED notes. If an auth test resolves to user reachableceo, stop — you are holding the founder's identity.
  4. TOTP seeds MUST be stored in Bitwarden (founder ruling 2026-09-08). Enroll TOTP and write the base32 seed to the vault item in the same breath — a seed that is not in the vault is treated as lost, because there is no admin 2FA reset on this Cloudron and no other record exists.
  5. Secrets live only in the vault (KNELSecretsManager sm CLI) — never in repos, command output, logs, or transcripts. Mask credentials in every command's visible output.
  6. Bookkeeping in the same turn: Redmine ticket on the provisioning program (#942 or its successor), cross-linked; vault item created;STATUS updated.

Scope check

Identity stand-up is the COO C2 lane's assigned program (founder directive, 2026-09-08). If your chat runs in another lane (SRE, security, OAM), confirm the founder assigned the stand-up to you before executing, or route a ticket to the COO C2 lane instead.

Admin lever: Cloudron API token

creds/cloudron holds a working admin API token (service user api-token, 64 chars). Use it as Authorization: Bearer $PASSWORD. Do NOT log into the dashboard interactively for admin work — the token is the compliant path.

scripts/cloudron-api.sh GET /users?limit=100     # list users (note uid- UUIDs)
scripts/cloudron-api.sh GET  /users/<uid>        # fetch one user object

Route notes (this Cloudron, 2026-09): users are addressed by uid-… UUIDs, not usernames; there is NO /api/v1/login (dashboard auth is its own OIDC flow); there is NO admin route to reset another user's 2FA — so the seed capture in step 2 is not optional.

Procedure

0. Prerequisites

  • Vault reachable: sm get creds/cloudron works (see references/vault.md).
  • For a NEW Cloudron user: the founder generates the invite, or you create the user via the admin token if the founder has authorized self-service.
  • A Redmine ticket to record the work (create one if none exists).

1. Cloudron account (unique password)

Verify the account exists and matches the identity email pattern (tsgstaff-<login>@turnsys.com for COO-line identities). Generate the unique password (32 chars), set it, and confirm a login works (step 2 proves it).

2. TOTP 2FA — enroll and CAPTURE THE SEED

Enroll TOTP via the profile self-service surface and write the base32 seed to the vault item before anything else:

sm setfield "<login> Cloudron" totp_seed "<BASE32SEED>"

If the identity already has 2FA enforced and no seed exists anywhere: STOP and ask the founder. There is no programmatic recovery (verified 2026-09-08). Compute codes offline when needed: scripts/totp.sh <seed>.

3. SSO first-login into each system

The interactive login is scriptable with a cookie jar — no browser needed. scripts/oidc-login.sh <system> runs the whole flow and leaves a session cookie jar at /tmp/oidc-<login>-<system>.txt. Entry points:

System SSO start point After login
GLPI https://cmdb.knownelement.com/plugins/singlesignon/front/callback.php/provider/1?remember=1 account auto-provisioned
Gitea https://git.knownelement.com/user/oauth2/cloudron account auto-provisioned
Discourse https://community.turnsys.com/auth/openid_connect account auto-provisioned
Redmine https://projects.knownelement.com/oauth account auto-provisioned (if the bounce in references/systems.md is hit, see that file)

Key gotcha (cost a debugging session — do not rediscover): the OIDC interaction login is POST /openid/interaction/<uid>/login with JSON body {"username": "...", "password": "..."} — the field is username, not login, and the response is {"redirectTo": ...} on success.

4. Mint per-system API keys (as the identity, via its session)

  • Redmine: My account → API key (visible to self); also returned by GET /users/current.json with the session.
  • Gitea: Settings → Applications → new token with write scopes; or POST /api/v1/users/<login>/tokens using the session (requires basic auth of the same user for token endpoints).
  • Discourse: user API key flow; or admin-generated key if an admin lever is ever provided (none currently is — do not use founder keys).
  • GLPI: My account → API token (personal token); pair with the app token from creds/glpi (password field) for REST calls.

Store each key in the vault item immediately (schema below).

5. Vault item schema — creds/<login>

One item per identity (NOT per system). Create it as a LOGIN item with:

  • login username = Cloudron username; password = the unique Cloudron password
  • URI = https://my.knownelement.com
  • fields: email, organization, role, reports_to, created, services, plus per-system: redmine_api_key, gitea_token, discourse_user_api_key, glpi_user_token, and totp_seed

Historical items that predate this schema (e.g. sec-*, sre-* shared- password items) are remediation targets under #942 — upgrade them when the owner lane assigns the work, do not replicate their shape.

6. Verify the trail

Prove each system knows the identity (expected: 200 + the identity's own name; a 200 from the wrong identity is a FAIL):

curl -sf -H "X-Redmine-API-Key: $KEY" https://projects.knownelement.com/users/current.json | jq .user.login
curl -sf -H "Authorization: token $TOKEN" https://git.knownelement.com/api/v1/user | jq .login

Then update the Redmine ticket (full URLs, [agent] <your-identity> note) and the workspace STATUS/memory.

References — read before touching the corresponding system

  • references/cloudron.md — admin API routes, OIDC interaction flow, 2FA.
  • references/systems.md — per-system SSO/API details and known breakage.
  • references/vault.md — sm CLI entry points, gotchas, fallbacks.
  • references/incident-log.md — what went wrong on 2026-09-07/08 and why, so the same failures are not re-derived.

Scripts

  • scripts/cloudron-api.sh METHOD PATH [JSON] — authenticated admin call.
  • scripts/oidc-login.sh <glpi|gitea|discourse|redmine> <login> — full SSO login via cookie jar; reads the identity's creds from the vault.
  • scripts/totp.sh <base32-seed> — current 6-digit TOTP code (pure bash).