authdb (20260618+2) unstable; urgency=high

  * file backend: File::read()/write() looped on rd/wr==0 as if more data
    were pending instead of treating it as EOF/failure; switch do/while to
    a while loop and throw on a zero-byte result so a stalled fd can no
    longer spin or silently truncate a record.
  * file backend searchValue() / export doExport(): record-walk advanced
    the read cursor by the record's own datasize without checking it
    against the buffer end, so a corrupt or oversized datasize could walk
    past end() (OOB read) or spin forever; now validates the computed
    offset before advancing and bails out of the loop otherwise.
  * AuthBackend::Guard: drop the shared->exclusive upgrade path and always
    take the exclusive lock; the upgrade bookkeeping (_Upgraded, mode
    tracking) was extra surface for lock-state bugs with no real
    shared-access use left.
  * RecordIndex: guard _nameIndex/_offsetIndex/_revision with a
    shared_mutex. build()/addEntry()/removeEntry()/invalidate() take a
    unique lock and findByName()/findByUuid()/findOffset()/listByType()/
    valid()/revision() take a shared lock; previously all of these were
    unsynchronized against concurrent index rebuilds.
  * Session / client-connection expiry: track last-access time instead of
    creation time (SessionData::_accessed, ClientConnections::_accessed,
    updated on every lookup). Long-lived but actively used sessions and
    client connections no longer get reaped by the 24h idle sweep just
    because they were created a day ago.
  * ClusterSession::getSession(): on cluster re-import, check for an
    already-present local session before appending the imported copy
    instead of unconditionally removing and replacing it — closes a race
    where a concurrent request's freshly-created local session could be
    clobbered by a stale cluster read.
  * Cluster backend: cache the g_Cluster pointer into a local before use
    in queueDataPush/pushManifestSync/fetchFromCluster/purge/constructor,
    avoiding a TOCTOU null-dereference if the global is reset by another
    thread between the null check and the use.
  * authdb.cpp: server now starts in plain HTTP mode when
    /AUTHDB/SSL/CERT and /AUTHDB/SSL/KEY are not configured (cluster mode
    still requires both and refuses to start otherwise); config key
    lookups (CERT/KEY/PLUGINS/PATH) are null-checked before use instead
    of dereferencing an absent key; domains with an empty storage path
    are skipped when populating the domain cache instead of constructing
    a broken backend for them.
  * admin.cpp/api.cpp: JSON API requests are now routed through
    ApiController from "/", "/admin/api" and "/settings/{domain}/api" in
    addition to the existing REST routes, so the client library can talk
    JSON directly to the admin domain without the HTML login flow.
  * admin.cpp: isSuperAdmin() no longer looks up membership in an
    admin.local "SuperAdmin" group; it now holds full admin rights if the
    session has every restrictive (default=false) GPO granted, removing
    the dependency on a specific group existing in the admin backend.
  * api.cpp GPOadd: check gpo.exists() before gpo.create() so re-adding
    an already-existing GPO no longer fails; create()/exists() failures
    are now caught and logged instead of silently reporting false.
  * api.cpp: client-key login without an explicit domain now resolves to
    the "admin.local" domain instead of leaving the session domain empty.
  * api.cpp editClient: use Client::edit() in place instead of
    remove()+create(), avoiding a window where the client record is
    briefly absent and a recreate under a fresh UUID could dangle
    existing references to the old id.
  * Client library: setUrl() now rewrites a bare "/" request path to
    "/settings/admin/api" to match the server's new JSON routing.
  * Node.js bindings: replace exports->GetIsolate() with
    Isolate::GetCurrent() in AuthDBConnectionJS::Init/AuthDBClientJS::Init
    for compatibility with current V8/Node headers.

 -- Jan Koester <jan.koester@tuxist.de>  Thu, 18 Jun 2026 05:52:44 +0200

authdb (20260502+1) unstable; urgency=medium

  * Rebuild against libnetplus 20260502+1 (QUIC performance improvements)

 -- Jan Koester <jan.koester@tuxist.de>  Sat, 02 May 2026 12:00:00 +0200

authdb (20260426+1) unstable; urgency=medium

  * file backend vacuum: check write/ftruncate/lseek return values,
    throw on failure instead of silently corrupting the database
  * file backend: check read/write return values in getRevesion()
    and newRevesion()
  * Fix -Wunused-result warnings: check mkdir/chown return values
    in authdb.cpp, log failures to stderr

 -- Jan Koester <jan.koester@tuxist.de>  Sat, 26 Apr 2026 17:00:00 +0200

authdb (20260424+8) unstable; urgency=medium

  * Cluster: use multi-store protocol for session data (store_id=1).
    Eliminates session_intercepting_store in favor of native server
    routing. Session clients use set_store_id() for proper separation.
  * Scrub no longer needs session group filtering — store_id routing
    ensures domain and session data are fully isolated.

 -- Jan Koester <jan.koester@tuxist.de>  Thu, 24 Apr 2026 00:00:00 +0200

authdb (20260424+7) unstable; urgency=medium

  * Client library: add ConnectionPool class for TCP connection reuse.
    PooledConnection RAII wrapper auto-returns connections to the pool.
  * Client library: add GPO result cache (5 min TTL) in GPOcheck() to
    avoid redundant network round-trips for repeated policy checks.

 -- Jan Koester <jan.koester@tuxist.de>  Thu, 24 Apr 2026 00:00:00 +0200

authdb (20260423+6) unstable; urgency=high

  * Cluster health monitor: replace sentinel store+remove with filesystem
    probe to prevent blocks.bin pollution (thousands of dead records per
    day).
  * Cluster::scrub(): store data BEFORE removing old copies to prevent
    data loss when re-store fails after remove.
  * Win32 compatibility for health probe (io.h, fcntl.h).

 -- Jan Koester <jan.koester@tuxist.de>  Wed, 23 Apr 2026 00:00:00 +0200

authdb (20260419+5) unstable; urgency=high

  * Prevent stale manifest overwrite: pushManifestSync now compares local
    revision against cluster revision before pushing. A node with an older
    revision (e.g. after restart before sync) can no longer overwrite a
    freshly imported or updated domain manifest in the cluster.

 -- Jan Koester <jan.koester@tuxist.de>  Sat, 19 Apr 2026 00:00:00 +0200

authdb (20260418+4) unstable; urgency=medium

  * Cluster: defer recovery_epoch bump until after scrub completes,
    preventing backends from fetching before data is rebalanced
  * Skip fetchFromCluster during active scrub/rebuild (use cached data)

 -- Jan Koester <jan.koester@tuxist.de>  Sat, 18 Apr 2026 23:30:00 +0200

authdb (20260418+3) unstable; urgency=medium

  * Cluster: vacuumAllNodes acquires scrub lock for cluster-wide
    synchronisation, preventing concurrent vacuum/scrub/rebalance

 -- Jan Koester <jan.koester@tuxist.de>  Sat, 18 Apr 2026 23:00:00 +0200

authdb (20260418+2) unstable; urgency=medium

  * Warmup all clients (pclient_, session_client_, scrub_client_) on init
    to eliminate first-request QUIC handshake latency

 -- Jan Koester <jan.koester@tuxist.de>  Sat, 18 Apr 2026 22:00:00 +0200

authdb (20260418+1) unstable; urgency=medium

  * Dedicated scrub_client_ for scrub/rebalance to avoid blocking
    push_worker and health_monitor via pclient_ mutex contention
  * Propagate scrub_running status to all nodes via GET_STATUS protocol
  * Set server rebuilding flag during scrub so remote nodes report REBUILD
  * Expose per-node scrub_running in admin cluster status API
  * Propagate mark_node_dead to scrub_client_

 -- Jan Koester <jan.koester@tuxist.de>  Sat, 18 Apr 2026 21:00:00 +0200

authdb (20260417+3) unstable; urgency=medium

  * Fix authdb unreachable during auto-vacuum by releasing Domain Cache
    Mutex before vacuuming

 -- Jan Koester <jan.koester@tuxist.de>  Fri, 17 Apr 2026 21:17:45 +0200

authdb (20260417+1) unstable; urgency=medium

  * Fix missing Username and GPO information in SessionData due to
    incomplete copy constructors
  * Add unit tests for Cluster Operations (session push, fetch, rebalance)

 -- Jan Koester <jan.koester@tuxist.de>  Fri, 17 Apr 2026 12:00:00 +0200

authdb (20260416+4) unstable; urgency=medium

  * Increase initial cluster retrieval timeout to 30s to fix startup
    delay failures

 -- Jan Koester <jan.koester@tuxist.de>  Thu, 16 Apr 2026 11:05:04 +0200

authdb (20260414+17) unstable; urgency=medium

  * Fix cluster retrieve blocking HTTP requests: fetchFromCluster now
    uses fire-and-forget async retrieve instead of synchronous 5s wait.
    Result is picked up on the next prefetch() call — no request ever
    blocks on a QUIC retrieve. First fetch after startup does a one-time
    synchronous wait so data is available for the first request.
  * Add revision guard: fetchFromCluster discards manifests with a
    revision lower than the cached revision, preventing data loss when
    a recovering node returns stale data.
  * Fix scrub data loss: scrub now validates retrieved data (size,
    AuthHeader marker) BEFORE calling remove(). Empty or corrupt
    retrieve results no longer trigger remove+store, which was deleting
    valid blocks from healthy nodes.
  * Fix import not persisted after restart: Import now calls flushSync()
    which does a synchronous push to the cluster, ensuring data is
    durably stored before returning the HTTP response. Previously
    pushManifest() was async (queued) and a restart would lose the data.
  * Add regression tests: PrefetchNeverBlocks, RevisionGuardNoDataLoss

 -- Jan Koester <jan.koester@tuxist.de>  Mon, 14 Apr 2026 22:00:00 +0200

authdb (20260412+16) unstable; urgency=medium

  * Dedicated session_read_client_ for session reads to avoid mutex
    contention with push worker holding session_client_->mutex_
  * Pre-connect read clients on startup (warmup) to eliminate
    first-request QUIC handshake + auth latency (2-3s per node)
  * Propagate dead node info to all read clients in health monitor

 -- Jan Koester <jan.koester@tuxist.de>  Sat, 12 Apr 2026 23:00:00 +0200

authdb (20260412+15) unstable; urgency=medium

  * Dedicated read_client_ for domain reads to avoid mutex contention
    with push worker and health monitor
  * Non-blocking constructor: no network I/O, uses static domain cache
  * Push worker: 3-retry limit for data pushes, sessions processed first
  * Orphaned group cleanup in scrub via knownDomainGroupIds()
  * Manifest embeds full buffer data (single QUIC call per fetch)

 -- Jan Koester <jan.koester@tuxist.de>  Sat, 12 Apr 2026 22:00:00 +0200

authdb (20260412+14) unstable; urgency=medium

  * Per-entity incremental cluster storage: only push changed
    entities instead of full domain buffer on unlock()
  * Manifest-based entity tracking with FNV-1a hash diffing
  * Support for queued entity removes in push worker

 -- Jan Koester <jan.koester@tuxist.de>  Sat, 12 Apr 2026 19:00:00 +0200

authdb (20260412+13) unstable; urgency=medium

  * Use libparitypp distributed scrub lock to prevent concurrent
    scrub/rebalance across cluster nodes
  * Skip scrub lock group in group listing

 -- Jan Koester <jan.koester@tuxist.de>  Sun, 12 Apr 2026 18:00:00 +0200

authdb (20260412+12) unstable; urgency=medium

  * Fix group distribution imbalance: session_intercepting_store no longer
    exposes session groups via list_groups/list_blocks/total_blocks/total_groups
    — prevents scrub/rebalance from writing session blocks into file store
  * Filter session groups in scrub() to avoid ghost group creation
  * Merge scrub + rebalance into single pass: remove() + store() for
    under-replicated groups fixes both missing and misplaced blocks
  * Add scrub_mutex_: scrub and rebalance cannot run concurrently
  * Add scrub_running_ status indicator to cluster status API and admin UI
  * Add admin API endpoint /api/clusterrebalance with Rebalance button
  * Add Scrub button to cluster status page

 -- Jan Koester <jan.koester@tuxist.de>  Sun, 12 Apr 2026 16:00:00 +0200

authdb (20260412+11) unstable; urgency=medium

  * Add periodic rebalance in health monitor: every ~5 minutes while all
    nodes are healthy, run rebalance() to fix blocks misplaced by
    transient store_stripe timeouts that never triggered degraded state

 -- Jan Koester <jan.koester@tuxist.de>  Sun, 12 Apr 2026 12:00:00 +0200

authdb (20260411+10) unstable; urgency=medium

  * Change outer API request lock from Exclusive to Shared — enables
    parallel read requests per domain
  * Add prefetch() to lock_shared() so cluster data stays fresh for reads
  * Fast-path prefetch: skip _FetchMutex entirely when data is fresh (<30s)
  * Add warmDomainCache() at startup: pre-loads all domain backends so
    API requests never need to lock _AdminBackend for domain resolution
  * Add evictDomainCache() called on domain removal to keep cache consistent
  * Integrate rebalance() into scrub to fix misplaced block distribution

 -- Jan Koester <jan.koester@tuxist.de>  Sat, 11 Apr 2026 20:00:00 +0200

authdb (20260411+9) unstable; urgency=medium

  * Fix cluster node blocking: add per-domain _FetchMutex to serialize
    concurrent prefetch() calls (prevents thundering herd on retrieve)
  * prefetch() uses try_lock — if another thread is fetching, wait for
    its result instead of issuing a parallel cluster retrieve

 -- Jan Koester <jan.koester@tuxist.de>  Sat, 11 Apr 2026 18:00:00 +0200

authdb (20260409+8) unstable; urgency=medium

  * Use immediate flush (threshold=0) for file_block_store to ensure
    blocks.bin durability after every write
  * Purge old session blocks from file store at startup by detecting
    SESB magic in block 0 shards, then vacuum to reclaim space

 -- Jan Koester <jan.koester@tuxist.de>  Wed, 09 Apr 2026 20:00:00 +0200

authdb (20260409+7) unstable; urgency=medium

  * Fix session data bloating blocks.bin: session_intercepting_store now
    routes session groups to memory-only session_store_ instead of
    file-backed inner store, using registered session group IDs
  * Fix session_client_ local node: use session_store_ instead of store_
    so self-node session shards stay in memory
  * Add startup vacuum to reclaim space from old session data in blocks.bin
  * Push worker drains all pending items per cycle instead of one-at-a-time,
    removes std::async spawn per push (sequential processing)
  * Mark session group IDs in cleanup loop for interceptor routing
  * Store interceptor_ pointer in Cluster for group registration

 -- Jan Koester <jan.koester@tuxist.de>  Wed, 09 Apr 2026 18:00:00 +0200

authdb (20260408+6) unstable; urgency=medium

  * New release

 -- Jan Koester <jan.koester@tuxist.de>  Wed, 08 Apr 2026 00:00:00 +0200

authdb (20260407+5) unstable; urgency=medium

  * New release

 -- Jan Koester <jan.koester@tuxist.de>  Tue, 07 Apr 2026 00:00:00 +0200

authdb (20260405+4) unstable; urgency=medium

  * Install index.h public header (fixes mediadb/blogi build against
    installed authdb: session.h includes index.h)

 -- Jan Koester <jan.koester@tuxist.de>  Sun, 05 Apr 2026 00:00:00 +0200

authdb (20260405+3) unstable; urgency=medium

  * Add cluster health monitor thread: probes peers every 10s, tracks
    degraded state, logs recovery
  * Skip cluster session fetch when degraded to avoid blocking logins
  * Reduce fetchSession lock timeout from 5s to 2s
  * pushSession synchronous again (cluster root cause fixed in paritypp)

 -- Jan Koester <jan.koester@tuxist.de>  Sun, 05 Apr 2026 00:00:00 +0200

authdb (20260405+2) unstable; urgency=medium

  * Fix AdminController using value copy of AuthBackend instead of
    reference — wizard data was invisible to login handler
  * Make backend push and session push non-blocking (background threads)
    so cluster timeouts don't block HTTP responses
  * Don't delete local session when cluster fetch fails (peers unreachable)
  * Return JSON 503 for /api requests when backend is uninitialized
    instead of wizard HTML page (fixes "unexpected character" in clients)

 -- Jan Koester <jan.koester@tuxist.de>  Sun, 05 Apr 2026 00:00:00 +0200

authdb (20260405+1) unstable; urgency=medium

  * Fix ClusterBackend constructor overwriting existing cluster data:
    no longer pushes empty header to cluster on startup (prevents
    wizard data loss when second node starts)
  * Retry fetchFromCluster up to 3 times during constructor init

 -- Jan Koester <jan.koester@tuxist.de>  Sun, 05 Apr 2026 00:00:00 +0200

authdb (20260405) unstable; urgency=medium

  * Vacuum cluster block stores on all nodes when cluster mode is active

 -- Jan Koester <jan.koester@tuxist.de>  Sun, 05 Apr 2026 00:00:00 +0200

authdb (20260404+3) unstable; urgency=medium

  * Rebuild against libparitypp 20260404+9 (stripe-based parity, 16KB chunks)

 -- Jan Koester <jan.koester@tuxist.de>  Fri, 04 Apr 2026 18:30:00 +0200

authdb (20260403+2) unstable; urgency=medium

  * Switch cluster from RAID1 mirroring to erasure coding (k+m shards)
  * Remove local in-memory session/data stores, use distributed storage
  * Sessions and data ops distributed via erasure-coded pclient->store/retrieve
  * listAllSessions enumerates groups across all cluster nodes

 -- Jan Koester <jan.koester@tuxist.de>  Fri, 04 Apr 2026 09:00:00 +0200

authdb (20260401) unstable; urgency=medium

  * Initial Debian packaging with multiarch support.

 -- Jan Koester <jan.koester@tuxist.de>  Wed, 01 Apr 2026 00:00:00 +0200
