- _remote_headers and /check used struct convenience properties that
read the global database; they now use the SessionContext / the handed
store (also fixes Remote-Credential carrying a struct repr instead of
the UUID).
- Replica availability: TTL clock starts at disconnect, not at last
message or failed reconnect; tight WS keepalive for prompt dead-peer
detection.
- Proxy preserves repeated Set-Cookie via raw headers; sync endpoint
does its own accept (wsutil decorator pre-accepts) and bypasses host
dispatch (server-to-server; satellite may use an out-of-domain
address).
- Admin-credential bootstrap warning skips remote domains.
Verified live with two instances (remote :4501, satellite :4402):
replica snapshot + events, 204 forward with Remote-* in <1ms, validate
write-behind landing on the remote, proxied logout with instant local
eviction, 503 after cache_ttl of disconnect, resync after remote
restart.
ApiDomain carries the remote block (sync token write-only, never
echoed); create/patch accept it, validated with the combined config
(auth host mandatory for remote domains). db.update_domain replaces
remote wholesale like the other domain fields.
DomainConfig.remote {url, token, cache_ttl, refresh_interval} marks a
domain as backed by a remote paskia instance (auth host required). The
remote publishes committed changes via struct store()/delete() hooks and
explicit emits in field-mutating operations into syncfeed, an in-RAM
sequenced ring buffer served over a token-gated WebSocket
(/auth/api/sync/ws, tokens from PASKIA_SYNC_TOKENS env). The satellite
keeps a plain DB replica per remote URL, applies snapshots/events,
enforces expiry locally, and writes session refreshes back over the same
channel. /validate refreshes locally with write-behind; /logout,
/set-session, /token-info and /auth/oidc/* are proxied to the remote
with the original Host header; logout also evicts from the replica.
Replicas go fail-closed (503) after cache_ttl of silence.
DB.session_ctx now reads only its own tables instead of struct
convenience properties that reach the global database, so a DB instance
(a read replica) is self-contained. session_ctx/session_context/
authz.verify/build_user_info take an explicit store parameter defaulting
to the local database; api.py endpoints resolve it from the dispatched
domain (Domain.store).
The restricted entry validates the session before rendering anything
(no load-time flash): 401/403 switches to the existing login component
in place of the profile, success renders the panel fully populated via
props, other failures show a minimal card with Back only. The dialog
drops the standalone page's heading and help text.
The overlay now tracks which dialog kind is open: auth-success from a
profile dialog resolves 'login', auth-back only rejects
AuthCancelledError for auth dialogs.
The Vite dev server now maps /paskia-js/ to the local paskia-js build
(the examples page's module import has 404'd since it was introduced,
leaving all buttons dead). Navigation actions on the examples page are
now plain same-window links so the flows' back navigation returns to
the page; added a Profile Summary button exercising profile().
The restricted page's early script now honors the URL theme parameter
before the cached profile theme, so a fresh server-provided override
wins and the host color scheme applies without a flash. After session
load, an empty profile theme clears the cache but keeps the URL
parameter in effect instead of reverting to the system default.
HostProfileView now renders the same centered frame card as the login
flows, whether shown full-page at /auth/ (host mode) or inside the new
#mode=profile restricted iframe. The component self-fetches its data
when the parent does not provide it, and emits back/logout so each
context reacts appropriately: the full page reloads, the iframe posts
auth-back / auth-logout to the host.
New profile() function opens the minimal profile in a compact dialog
iframe and always resolves ('logout' | 'back'), keeping the auth flow's
resolve/reject contract separate and unchanged. The overlay now injects
the host page's computed color-scheme into the iframe URL theme param
when the server has not provided one.
The **.{rp-id} wildcard was only added when the resulting origins dict
was empty, so a legacy database with a dedicated auth host but no
configured origins ended up allowing only the auth host.
- paskia migrate accepts an rp-id, a legacy *.paskiadb path, or a
current-format *.kantadb path; with an existing target database the
incoming data is merged (uuid-keyed records make conflicts a non-issue,
domains merge per rp-id with a union of origins)
- Migration transactions are labeled migrate:cli:{rp-id} (slash-joined
for multi-domain sources) instead of 'bootstrap'
Break the monolithic admin dialog component into a thin dispatcher plus
one component per dialog type under admin/dialogs/, with a shared
AdminDialog frame (Modal wrapper, title, error and Cancel/Save actions).
No functional change. Also drop two unused input refs (nameInput,
displayNameInput).
- Print the box only when serving; 'paskia init' output is the reset
link, which already carries the full auth site URL
- Domain row is always 'Domain:'; rows beneath it are unlabeled,
belonging to the domain by position
- Multi-domain: each domain's auth site printed as a full clickable URL
(auth host root when marked, else <site>/auth/)
- In-domain sign-in sites collapsed to a one-line summary
('example.com and all subdomains, +N sites'); related origins are few
and surprising, so always listed in full
- Serve multiple domains (RP IDs) from one instance: host-based dispatch,
per-domain credentials and sessions, domains managed at runtime in the
admin UI — previously one RP per instance
- Cross-domain sign-in via Related Origin Requests: per-domain related-origins
list with a served .well-known/webauthn document
- Explicit per-domain origin lists with shell-glob wildcards (**. for apex +
any subdomain depth, *. for one level), editable in the admin UI with
validation and self-lockout guards
- Per-domain auth hosts: the account/admin UI can live on a different host
per domain, no longer confined to subdomains of a single RP
- CLI: 'paskia init <rp-id [rp-name]' initializes or adds a domain to an
existing database; 'paskia migrate' converts legacy databases
BREAKING CHANGES (v2.0):
- Database schema: config is now per-domain and credentials/sessions carry
an rp_id — existing databases must be converted with 'paskia migrate'
- Origins are now explicit: main implicitly allowed every subdomain of the
RP; configure '**.' origins to reproduce that behavior
- CLI: the flat '--rp-id/--rp-name/--origin/--auth/--save' flags are
replaced by the 'init' and 'migrate' subcommandsReviewed-on: #4
/auth/api/forward?public=1 passes requests through with a Remote-Public
header (anonymous/forbidden/authenticated) instead of 401/403, so routes
can allow anonymous visitors while still identifying logged-in users.
Reauth (max_age) still requires the auth flow. Documented in Headers.md,
api/forward.md, Integration.md and all proxy guides.
perm=a|b+c now means (a or b) and c; repeated perm args remain ANDed.
Out-of-spec values (empty alternatives, chars outside the scope charset,
stray %2B) are rejected with 400 instead of being silently misparsed;
extra spaces between groups are tolerated. Forward endpoint 400/500
details name /auth/api/forward as origin without echoing query args.
Wildcards are now filename-like: * stays within a :- or /-separated
segment, ** spans segments, partial segments allowed. Slash added to
allowed scope characters for path-based permissions.
The vendored db/logging module duplicated what kanta now provides:
diff formatting, UUID-to-label resolution via logfmt callbacks, unsafe
character filtering and value truncation. Censoring of oidc.key material
moves into the format_log_uuid logfmt callback in db.lifecycle, taking
care to hide only the value, not the 'key' path component itself.
- Make use of its new features and cleanup our interfacing and init/shutdown processes and migrations
- Clean up circular deps, simplify app init
- Add specific pytest for CLI main to cover the changes