# Changelog All notable changes to Armature are documented here. ## v0.6.9 — Session Lifetime Config Admin-configurable session durations, "keep me logged in" opt-in, and optional idle timeout. Completes the auth hardening started in v0.6.8. ### Added - **Admin session settings**: `session.access_token_ttl` (default 15m, clamp 5m–60m) and `session.refresh_token_ttl` (default 7d, clamp 1h–90d) stored in `global_settings`. New `LoadSessionConfig()` helper reads and clamps values with `parseDurationString()` supporting `m`, `h`, `d` suffixes. - **"Keep me logged in" checkbox**: Login form opt-in. Checked = full `refresh_token_ttl`. Unchecked = capped at 24h. Cookie `max-age` tracks whichever lifetime was chosen. `keep_login` flag stored on refresh token row. - **Config-driven token generation**: `generateTokens()` reads TTLs from `global_settings` instead of hardcoded `15*time.Minute` / `7*24*time.Hour`. Response includes `expires_in` and `refresh_expires_in` (seconds) so the client schedules refresh correctly. - **Idle timeout (optional)**: Admin-toggleable (default off). Server checks `last_activity_at` on refresh-token row; rejects if gap exceeds `session.idle_timeout`. Client SDK pings `POST /api/v1/auth/activity` on click/keydown (debounced, max 1/min). - **Admin Settings > Session section**: Dropdowns for access TTL, refresh TTL, and idle timeout with toggle. - **7 new tests**: Duration parsing, clamping (low/high), defaults, invalid values, empty idle timeout. ### Changed - `CreateRefreshToken` store method now accepts `keepLogin bool` parameter; both Postgres and SQLite implementations updated. - `GetRefreshTokenInfo` returns `RefreshTokenInfo` struct with `UserID`, `KeepLogin`, `LastActivityAt` for idle-timeout decisions. - SDK `auth.login()` accepts optional third `keepLogin` parameter; cookie max-age derived from server `refresh_expires_in` response. - SDK boots activity tracking after successful auth boot. ## v0.6.8 — Cookie Fix + UI Hardening Roadmap ### Fixed - **Session cookie max-age bug**: `arm_token` cookie was set to 15 min (matching access token) while refresh token lasted 7 days. Cookie now matches refresh token lifetime so Go SSR middleware can serve page shells while JS refreshes the access token client-side. ### Added - **ROADMAP-UI.md**: Detailed UI hardening roadmap (v0.6.9–v0.6.15) covering session config, viewport foundation, CSS deduplication, extension CSS isolation, responsive layout, visual polish, and automated usability survey gate. ## v0.6.7 — Native mTLS End-to-end mutual TLS without a reverse proxy. Targets systemd+podman deployments where the Go binary terminates TLS itself. ### Added - **`TLS_MODE` config**: Three values — `none` (default, plain HTTP), `server` (TLS, no client cert), `mtls` (mutual TLS, client cert required). Independent of `AUTH_MODE`. Combinations: `server`+`builtin` for HTTPS with password auth, `mtls`+`mtls` for full mTLS identity, `none`+`mtls` for proxy-terminated (existing behavior). - **TLS server mode**: Binary calls `ListenAndServeTLS` directly when `TLS_MODE` is `server` or `mtls`. TLS 1.3 minimum, no cipher suite configuration. Config: `TLS_CERT`, `TLS_KEY`, `TLS_CA` path env vars. - **`MTLSNativeProvider`**: New auth provider that reads `r.TLS.PeerCertificates[0]` directly — no header trust. `Subject.CommonName` becomes username, `sha256(cert.Raw)` becomes `external_id`. Auto-provisions users identically to the proxy provider. Selected when `AUTH_MODE=mtls` and `TLS_MODE=mtls`. - **Shared mTLS helpers**: `ParseDN()`, `FingerprintCert()`, and `resolveOrProvision()` extracted to `mtls_helpers.go`. Both proxy and native providers consume the same user resolution logic. - **Peer TLS config**: `BuildPeerTLSConfig()` constructs a `*tls.Config` for outbound node-to-node connections (forward-looking — cluster registry is currently DB-backed with no HTTP peer calls). - **`armature-ca.sh`**: Shell wrapper around openssl for cert provisioning. Three commands: `init` (CA keypair), `issue-node` (365d, ServerAuth + ClientAuth EKU), `issue-user` (90d, ClientAuth only). All ECDSA P-256, PEM output. - **12 new tests**: `FingerprintCert` determinism, native provider unit tests (nil TLS, empty certs, no CN, email extraction), TLS integration tests (no cert rejected, wrong CA rejected, expired cert rejected, valid cert accepted, peer certificate visibility). ### Changed - **`MTLSProvider` → `MTLSProxyProvider`**: Renamed for clarity. Config type `MTLSConfig` → `MTLSProxyConfig`. Constructor `NewMTLSProvider` → `NewMTLSProxyProvider`. File `mtls.go` → `mtls_proxy.go`. ## v0.6.6 — Final Hardening Final pass before public release. Security, correctness, and developer experience. No new features — only fixes, validation, and cleanup. ### Added - **`ValidateManifest()` gate**: Centralized manifest validation function (`package_validate.go`) called at both upload-install and bundled-install time. Catches malformed manifests early with clear error messages. 12 unit tests covering all type constraints and edge cases. - **Extension dependency auto-activation**: Installing a package whose `dependencies` list an uninstalled library will auto-install that library from the bundled packages directory. If the dependency is not bundled, the error message lists exactly what's missing. - **`OptionalAuth` middleware**: New page middleware for workflow visitor routes. Authenticates if a token is present, allows anonymous pass-through otherwise. Replaces the stale `AuthOrRedirect` TODO for Session routes. - **Package signing schema reservation**: `signature` field accepted in manifest (no-op). `PACKAGE_VERIFY_SIGNATURES` env var logs warnings for unsigned packages when enabled. No cryptographic verification yet — schema slot reserved to prevent a breaking change later. - **ICD/SDK runner v0.6.x coverage**: Smoke tier adds metrics, cluster, backups, docs, and OpenAPI JSON endpoints. SDK admin domain adds metrics, cluster, and backups dual-path tests. ### Fixed - **OIDC nonce validation**: ID token nonce claim is now validated against the stored authorization request nonce. Previously the nonce was generated and stored but never checked on callback — a token replay/substitution vulnerability. ### Changed - **Schema migration stub**: `RunSchemaMigrations()` no longer pretends to work. Replaced with a log-only function documenting that only additive schema changes are supported. Downgrade rejection preserved. - **Session middleware TODO resolved**: `main.go` Session slot now uses `OptionalAuth` instead of `AuthOrRedirect`. OIDC nonce TODO and migration stub TODO also resolved. ## v0.6.5 — Renderer Pipeline + Docs Rewrite Lifts block rendering to a kernel SDK primitive so all surfaces share one markdown pipeline. Rewrites docs for external audience. Adds CONTRIBUTING guide and extension tutorial. ### Added - **`sw.renderers` SDK module**: Kernel-level renderer registry. Extensions register block renderers (fenced code blocks) and post renderers (DOM post-processing) once; all surfaces consume via `sw.markdown`. - **`sw.markdown` SDK module**: Unified markdown rendering via `marked` v16 (vendored). Lazy-loads `marked` + DOMPurify. Custom code hook delegates fenced blocks to `sw.renderers`. Wikilink tokenizer (`[[Page Name]]`) built in for all surfaces. - **Browser extension script loader**: Server injects `