Landing blocker for #61 and #63: the deployment checkout is 12 commits behind and `merge --ff-only` refuses while tracked paths are dirty. Discard is not available here (workspace policy: no `checkout`/`stash` on shared state), so the dirt is committed. - `agent/APPEND_SYSTEM.md`: the point-of-use / one-work-log / PR-link doctrine, already byte-identical to `origin/main` (`cmp` against the trunk blob: no difference). No new prose — the commit exists so the trunk merge can proceed. - `agent/config.yml` and `agent/managed-skills/**`: the machine-live tier the heartbeat owns (AGENTS.md § Live-sync tiers). `ALLOW_SECRETS=1` was used, deliberately and after auditing the staged diff by hand: the only credential-shaped strings in it are two copies of `AKIA0000000000000EXAMPLE` — the inert all-zero AWS example id — and they appear inside the two skill pages that teach that exact placeholder shape. This checkout is still running the pre-#61 hook; the merged gate (value-scoped placeholder filter, `71601f7`) exempts this value, so the refusal dies with this landing. No `--no-verify` was used: the gate ran and its verdict is recorded here. Refs #61
3.6 KiB
name, description
| name | description |
|---|---|
| authentik-session-ttl-query | Query or change authentik login-session TTL on Dankclaw via ak shell — the string-typed UserLoginStage.session_duration trap, the apply logic, and why SESSION_SAVE_EVERY_REQUEST is unavailable. |
Change or audit how long an authentik login session lives (Dankclaw authentik-server, build 2026.x). The real knob is a DB stage row, not env — and the field format will bite you.
Where the TTL actually lives
- Run program through raw stdin:
ssh dankclaw 'podman exec -i authentik-server ak shell' <<'EOF' ... EOF 2>&1 | sed -n '/objects imported/,$p'. - Get models by label, NOT import paths (
from authentik.core.models.setting import ...fails;authentik.core.modelsis not a package; GlobalSettings is NOT a model in 2026.x):from django.apps import apps; ULS = apps.get_model("authentik_stages_user_login", "UserLoginStage") - The password sign-in path is flow
default-authentication-flow→ stagedefault-authentication-login(FlowStageBinding FK istarget, notflow). Other stagesdefault-source-authentication-login/default-source-enrollment-logingovern external-IdP/enrollment logins only.
THE FORMAT TRAP (will break login if wrong)
UserLoginStage.session_duration is string-typed in authentik's own idiom: "days=7" / "seconds=2".
- Assigning a
datetime.timedeltastores Python'sstr(timedelta)='7 days, 0:00:00', which authentik's parserauthentik.lib.utils.time.timedelta_from_stringREJECTS (ValueError: not enough values to unpack). Left like that, login crashes computing the expiry. - Correct write:
s.session_duration = "days=7"; s.save(update_fields=["session_duration"]) - Verify it parses:
from authentik.lib.utils.time import timedelta_from_string as P; P(s2.session_duration)→7 days, 0:00:00. A singlekey=valuetoken only;"days=7 seconds=0"also errors.
How it applies (stage.py:77-87, verified)
delta = timedelta_from_string(session_duration); if remember: delta += remember_me_offset; then set_expiry(0) if delta==0 else set_expiry(delta). So any positive duration applies to EVERY login (remember-me only adds on top), and set_expiry(<positive>) overrides SESSION_EXPIRE_AT_BROWSER_CLOSE → persistent cookie. Existing sessions keep their old expiry; new logins only.
SESSION_SAVE_EVERY_REQUEST (sliding window) is NOT available
/authentik/root/settings.py reads SESSION_COOKIE_AGE from CONFIG.get("sessions.unauthenticated_age","days=1"), sets SESSION_EXPIRE_AT_BROWSER_CLOSE=True, and references SESSION_SAVE_EVERY_REQUEST nowhere → no AUTHENTIK_* env or config.yml key can flip it. Enabling a sliding window means patching the vendor image; instead raise the login-stage duration (absolute, supported).
Audit the rest of the expiry surface (live values)
- framework:
settings.SESSION_COOKIE_AGE,SESSION_SAVE_EVERY_REQUEST - OAuth2 RPs:
apps.get_model("authentik_providers_oauth2","OAuth2Provider")→access_token_validity/refresh_token_validity(this fleet: access 1h [embedded-outpost 24h], refresh 30d) - proxy claim cache:
authentik_providers_proxy.ProxySession.expires(observed ~1–24h; silent re-auth, not a logout) - tenant:
authentik_tenants.Tenant.default_token_duration(hand API tokens, not sessions)
Notes
- These stage/provider rows are DB state, NOT repo-managed → a blueprint re-import or fresh rebuild reverts them; drop a setup script/blueprint in the repo if reproducibility matters.
- Don't probe the guarded vhost for the agent API and don't confuse Anubis' 168h challenge cookie with the authentik login session.