Callers never see stores: session_ctx/verify/user-info resolve the store
from the request host via satellite.store_for_host; session refresh and
logout eviction are dispatch functions too (satellite.refresh_session /
evict_session). API handlers keep one code path plus forward_request
one-liners; proxy.py folds into satellite.py; Domain.store and the
store parameters are gone; 503 comes from the dispatch point as a plain
HTTPException.
The sync protocol drops replay/generation/seq: snapshots are small, so
every connect starts from a full snapshot and a single ordered WebSocket
cannot gap; a slow subscriber is dropped and resyncs. The satellite
reconnects every refresh_interval to reconcile drift.
- _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.
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'
- 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.
- 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
Allows Paskia to authenticate the user to a client site.
- User friendly client registration flow on the admin app
- Redirect-based authentication flow (per spec)
- Backchannel logout both ways to keep sessions synchronized
- Groups integrated with Paskia's permission system
- Adds email, preferred username and telephone fields on user profile
- All new user basic info layout to show the new information, better looks
- API and DB structures redesigned
- Various unrelated fixes to theming and layout