Releases: voidvec/fulla
Release list
Fulla v1.3.0
[1.3.0] - 2026-09-14
⚠️ Breaking (client identity)
- The first-party OAuth2 clients are renamed:
vue-client→fulla-portal(user portal) andadmin-console→fulla-admin-console(admin console). Deployments upgrading from ≤ 1.2.0 must rename the existing rows once so consents and tokens keep pointing at a live client (run before or right after starting the new version — the startup seeder fills in whatever is still missing):
UPDATE oauth2_clients SET client_id = 'fulla-portal' WHERE client_id = 'vue-client';
UPDATE oauth2_client_scopes SET client_id = 'fulla-portal' WHERE client_id = 'vue-client';
UPDATE oauth2_clients SET client_id = 'fulla-admin-console' WHERE client_id = 'admin-console';
UPDATE oauth2_client_scopes SET client_id = 'fulla-admin-console' WHERE client_id = 'admin-console';Frontend deployments compiled with VITE_CLIENT_ID=vue-client must rebuild with the new id (the Docker images ship fulla-portal by default).
⚠️ Breaking (environment variables)
FULLA_VUE_CLIENT_SECRET/FULLA_VUE_REDIRECT_URIare renamed toFULLA_PORTAL_CLIENT_SECRET/FULLA_PORTAL_REDIRECT_URI; the old names remain honored as deprecated aliases. New:FULLA_ADMIN_CONSOLE_REDIRECT_URIregisters the admin console's production redirect URI.
Added
- Startup seeding of config-declared OAuth2 clients (#204): production had no path from the plugin config's
clientsblock to the database, so a freshly deployed server failed login at code issuance with3001. A startup seeder now inserts config-declared clients (both first-party clients ship declared in all configs) idempotently — production deployments no longer need any manual client SQL.
Fixed
- Registration no longer leaves users guessing about email verification: the success screen now persists, names the address the verification email was sent to, and links to login explicitly.
Commit-level changes
- oauth2: Rename first-party clients to fulla-portal / fulla-admin-console
- oauth2: Declare fulla-portal + fulla-admin-console in all shipped configs
- frontends: Portal client id becomes fulla-portal, admin becomes fulla-admin-console
- user: Tell users to verify their email after registration
- main: Correct OAuth2Plugin lookup and expose clients seed config publicly
Fulla v1.2.0
[1.2.0] - 2026-09-14
Added
- website: Docusaurus blog module + launch-post draft frame
- blog: Launch-post charts — five-scenario + cold-start, reproducible
Fixed
- security: 2026-09 audit — P0 batch (nonce, revocation, issuance guards)
- ci: PR #197 review — migration/api baselines, nonce length cap, branch coverage
- ci: Strict-JSON configs — drop jsoncpp-only // comments
- deploy: Rehearsal findings — migrate build section, doc drift, bootstrap password passthrough
- deploy: PR #199 review — --build on migrate run, AUTO_MIGRATE drift sweep, zh-CN guide sync
- auth: Send verification email on registration + unauthenticated resend (issue #198)
- auth: Guard email-verification against memory-storage mode
Documentation
- support: Route SDK users (Python/Go clients + C++ embed) to the right entry points
- blog: Launch post — full draft prose (all five sections)
Testing
- auth: Leave-no-trace cleanup in email-verification tests
Other
- ci: Normalize test names to Unit_OAuth2_ prefix (naming gate)
- ci: Test names follow Unit__ convention (naming gate)
Verification
Images are signed with cosign (keyless, GitHub OIDC):
cosign verify ghcr.io/voidvec/fulla-backend:1.2.0 \
--certificate-identity-regexp 'https://github.com/voidvec/fulla/.github/workflows/release.yml.*' \
--certificate-oidc-issuer https://token.actions.githubusercontent.comSPDX SBOMs for the three images and the source tree are attached,
as is the SDK tarball checksum:
sha256sum -c fulla-sdk-1.2.0-linux-x86_64.tar.gz.sha256Fulla v1.1.1
[1.1.1] - 2026-09-10
Added
- frontend: Add en/zh-CN i18n via vue-i18n curated catalogs (ADR-0013)
- Decision items #88-3 / #105 / #110 — client_id logout identification, coverage ratchet gate, signing-key rotation keystore
- frontend: Reactive error locale, AOT i18n precompile + size gate, image build smoke
- claude: Distill session experience into skills, rules, commands and hooks
- claude: Add deployment runbook skill for docker env split and WSL prod staging
Fixed
- admin: Nginx absolute_redirect off — redirects dropped the mapped host port
- release: Splice curated breaking-change entries into release notes
- frontend: Address PR #156 review — locale switcher reactivity, a11y, hardening
- Issues batch 2 — #122/#144/#145/#146
- #157 review: Session demotion after forced change, lockout on wrong old_password, dual-key revoke, mfa_pending ordering, gate assertions
- tests: Drop unused execSql helper breaking -Werror=unused-function on GCC/Clang
- #157 review round 2: Erase-first all re-writable session keys, lockout read side, portal-aware redirect, RNG fail-closed, JSON-only change endpoint
- i18n: Integrate the forced-password-change form into the #156 catalogs
- #176 review: RSA-only key guard, unopenable-pem hard fail, rotation-proof script assertions, JWKS cache note, workflow paths
- ci: De-flake the expiry boundary test; make the gcov report pass loud and locatable
- #176 review round 2: POST end_session client_id spec, keystore-dir gate test, baseline-keyed ratchet exemption, keystore filename hardening, changelog
- ci: Grant pull-requests: read on the frontend call job for the image-smoke paths filter
- frontend-user: Isolate image build type-check from cross-app test imports
- oauth2: Device approval crash — shared-callback fix in DeviceCodeService
- health: /health/ready Redis timeout guard; dashboard survives Redis outages
- portal: Resume OAuth after login; drop zombie sessions; stop burning codes
- identity: Generate usernames for blank registrations + backfill NULL rows
- config: Point FULLA_VUE_CLIENT_SECRET at the OAuth2Plugin client entry
- config: Drop the hardcoded vue-client secret from config.prod.json
- server: Address PR #180 review M1/M2/M4/M5/M7
- portal: Address PR #180 review M3/M6/M9
- claude: Refresh stale facts and repair commit/format hooks
- release: Codex review — SDK guide tag/push mismatch + regenerate openapi.json
Documentation
- readme: Add PyPI and Go SDK badges
- changelog: Fix compare links after v1.1.0
- changelog: English for the v1.1.0 breaking entries
- changelog: Unify the changelog in English
- community: Add community health files (#106)
- root: Neutralize vendor-specific CLAUDE.md / CODEBUDDY.md (#113)
- issues: Add internal tracking issue template
- website: Fix stale MIT references; land ubuntu-24.04 doc sync
Testing
- scripts: Dedupe full-test manual endpoint layer via ctest JUnit proof (#119)
- identity: Cover the new blank-username branches (coverage ratchet)
CI/Build
- deps: Bump the actions-minor group across 1 directory with 2 updates
- deps: Bump actions/upload-artifact from 4.6.2 to 7.0.1
- deps: Bump docker/build-push-action from 6.9.0 to 7.3.0
- deps: Bump actions/setup-python from 5.6.0 to 7.0.0
- deps: Bump actions/download-artifact from 4.3.0 to 8.0.1
Miscellaneous
- docker: Shrink the build context (node_modules, dist, docs, website, clients, benchmarks)
- migrations: Ratify V029 into the migration-check baseline
- api-diff: Ratify PortalUrl.h (additive) and the NOT NULL model regen
- coverage: Seed the ratchet baseline from PR #176's own green CI run
- deps-user: Bump the user-frontend-minor group
- deps-admin: Bump the admin-frontend-minor group
- deps: Coordinated frontend majors — TS7 (side-by-side TS6 API), vite 8, eslint 10, vue-router 5, plugin-vue 6
- config: Dual loopback redirect URIs for vue-client; run_server path fix
- hygiene: Scrub developer-local absolute paths from tracked files
- api-diff: Ratify baseline for the env-override path fix
- migrations: Ratify baseline for V030/V031
- api-diff: Ratify baseline for DeviceCodeService callback signatures
- claude: Remove dead OpenSpec commands and superseded release skill
Verification
Images are signed with cosign (keyless, GitHub OIDC):
cosign verify ghcr.io/voidvec/fulla-backend:1.1.1 \
--certificate-identity-regexp 'https://github.com/voidvec/fulla/.github/workflows/release.yml.*' \
--certificate-oidc-issuer https://token.actions.githubusercontent.comSPDX SBOMs for the three images and the source tree are attached,
as is the SDK tarball checksum:
sha256sum -c fulla-sdk-1.1.1-linux-x86_64.tar.gz.sha256Fulla v1.1.0
[1.1.0] - 2026-09-01
⚠️ Breaking (security hardening)
⚠️ Breaking (security hardening) — legacy password hash rejected by default (#103):auth.allow_legacy_hashnow defaults to false (a missing config key is also treated as false); unsalted SHA-256 hashes are rejected outright on every path, including old-password verification during a password change. Existing legacy-hash users should migrate via email/admin-initiated password reset (a reset writes a PBKDF2 hash, completing the migration) or by temporarily reopening the window (login transparently re-hashes the credential); note that changing the password alone cannot self-migrate (the old-password check runs through the retired branch as well). Policy rejections do not count toward account lockout. Dev seeds and all scripts now use PBKDF2 (same passwords);bench_users.sqlmoved tobenchmarks/fulla/seed(no longer shipped in the production image).⚠️ Breaking (security hardening) — real WebAuthn signature verification (#142): registration and authentication now perform genuine W3C WebAuthn Level 2 verification (ES256 only, fmt="none"; challenge binding with 300 s TTL and unconditional consumption; UV required; signCount clone detection). V028 purges the existing credential table (all pre-existing rows are unverified client-asserted material — users must re-register their passkeys);webauthn.rp_originsis now a required config key (the finish endpoints fail closed when it is absent); the old request-body contracts ({credential_id, public_key}registration / bare credential_id login) are rejected, closing the hole that allowed impersonation with nothing but a credential_id. New error codesWEBAUTHN_INVALID_ATTESTATION(3015) andWEBAUTHN_CHALLENGE_MISMATCH(3016).⚠️ Breaking (security hardening) — social login response shape change (#70):/api/{google,wechat}/loginnow issues a first-party token pair instead of returning the provider profile (token rows store public_sub — this also fixes existing GitHub tokens 404ing across the /api/me family); the GitHub endpoint spec was corrected accordingly. All three providers now auto-create an account on first login (global switchexternal_auth.auto_create_on_first_login, default true; when disabled, an unlinked login returns 403AUTH_SOCIAL_ACCOUNT_NOT_LINKED(5003)); issuance is recorded via theSOCIAL_LOGIN_TOKEN_ISSUEDaudit event (not a consent row).
Added
- frontend: P0 "Machined Trust" foundation — OKLCH tokens, palette codemod, fonts, brand mark
- frontend: P1 component layer — UI kit retune, DData machine voice, user kit adoption
- frontend: P2 trust facades — vault-door auth forms, consent receipt, admin login
- frontend: P3 data plane — mono metrics, table density, device giant input, user detail pills
- frontend: P4 dark theme + motion — theme store, ramp inversion, stamp pulse
- security: #103 retire legacy unsalted-SHA256 password verification
- social: #70 Google/WeChat logins consume subject mappings and issue first-party tokens
- security: #142 real WebAuthn attestation/assertion verification (ES256, fmt none)
Fixed
- review: PR #148 review — C1/C2 criticals, I1-I6 importants, test gaps
- identity: #143 local subject mapping three-layer fix for consent 500
- test: Fallout from the full-suite run — bootstrap salt, packaging dep, quoting, regression formula
- review: PR #149 adversarial-review findings — async retry lifetime, passkey liveness gate, UV alignment
- build: Cross-platform conan.lock — hand-add libcbor entry instead of regenerating
- test: WERROR fallout in WebAuthnHttpTest — merged #include line, dead placeholder helper
- test,warnings: Cross-compiler blind spots found on the local CP936 toolchain
- scripts: CREATEDB self-heal + precise guidance in setup-database
- scripts: Root-cause classification for setup-database failures
- scripts: Name both causes in the undroppable-database hint
- review: External PR review findings — crossOrigin gate, challenge sweep, signCount CAS, client registration check
- ci-scripts: Fail-soft guard when the staged tests config is missing before the config.ci.json swap
- release: Docker builder base ubuntu:22.04 -> 24.04 (GCC 11 linker ICE on libcbor)
Documentation
- frontend: Design-tokens header reflects the dark-override seat and theme wiring
- changelog: Unreleased entries for #143/#103/#70/#142
Testing
- e2e: Stub /oauth2/token in the google-callback test
- e2e: Poll for the google-callback token storage instead of a fixed sleep
- e2e: Assert the google-callback flow against the real token-storage contract
Other
- frontend: Align the new SocialCallbackPage error box with the #148 token system
Verification
Images are signed with cosign (keyless, GitHub OIDC):
cosign verify ghcr.io/voidvec/fulla-backend:1.1.0 \
--certificate-identity-regexp 'https://github.com/voidvec/fulla/.github/workflows/release.yml.*' \
--certificate-oidc-issuer https://token.actions.githubusercontent.comSPDX SBOMs for the three images and the source tree are attached,
as is the SDK tarball checksum:
sha256sum -c fulla-sdk-1.1.0-linux-x86_64.tar.gz.sha256Fulla v1.0.1
[1.0.1] - 2026-08-30
License
- License change: MIT -> AGPL-3.0. Starting with this version the project
is licensed under the GNU Affero General Public License v3.0; the v1.0.0
release remains MIT. The core stays fully open source — individuals,
study, self-hosting and internal use are unaffected. Vendors offering the
software (modified or not) as a hosted service must release their
modifications per AGPL-3.0 §13, or obtain a commercial license
(open-core: enterprise features are distributed separately). A CLA is
required for contributions so the project can keep dual-licensing.
Added
- oauth2: VerifyJwt follow-ups — expectedAud, nbf, strict base64url, verifyAndDecode, ephemeral kid (#87)
- config: Production gates for signing key and weak client secrets (#102)
- identity: Unlink guard counts WebAuthn credentials + post-delete race recheck (#73)
- social: Disable providers with empty/placeholder credentials (#111)
- social: Server-side one-time link state, closing provider-code injection (#71)
- ops: Schedule ensure_audit_partitions() on the cleanup cycle (#83)
- security: Consent authentication gates + admin bootstrap + password policy (IAM hardening tranche 1)
Fixed
- tests: Repoint FULLA_DOC_API_REFERENCE to docs/domains/ after IA restructure
- docs: Keep error-table message strings catalog-exact in the EN api-reference
- website: Localize sidebar/navbar/footer via official i18n files; categorize ADRs
- oidc: End_session array-aud candidates + enveloped 400s (#88 items 1-2)
- logout: Backchannel notifier matches both user_id subject forms (#82)
- CI regressions from batch-1 (WERROR getenv, fallback guard scope, N2 wait, doc table, ignore wording)
- tests: OAuth2Tests fail-fast 0xc0000409 — three causes, all test-side
- ci: Ratify RateLimiter.getConfig() in api-diff baseline; exact-prefix oasdiff ignore line
- ci,docs: Coverage workflow drogon_ctl PATH, API/deployment docs sync (#105, #112)
- ci: Sanitizer link flags PUBLIC; memory-mode-safe partition test guard
- mfa: Base32 bit-accumulation overflow (UB) found by the first UBSan run (#104)
- ci,ubsan: TotpUtils drogon-lib UB + Redis AUTH in sanitizer/coverage workflows
- ci,asan: Ubuntu 24.04 + RateLimiter UAF + coverage div-zero guard
- benchmarks: GC jitter root cause analysis — WSL2 I/O scheduling pauses, not runtime GC
- ubsan,werror: Base64UrlDecode shift UB + GCC 13 redundant-move
- docker: Add WeChat env override, FULLA_VERSION, and consistency comments
- config: Add /oauth2/device/approve rbac rule in all five configs
- admin: Complete MFA login, device approval page, dashboard/log/user honesty fixes
- user: WebAuthn register contract, real logout, consent user_id, backup codes
- frontends: Consume MFA PKCE verifier on success, not on entry
- frontends: Address PR #140 review — logout self-revoke race, mock envelopes, RT retention
- tests: Drop unused constants breaking -Wunused-const-variable on clang
- config: Move new auth knobs under custom_config.auth (repo convention)
- review: Consent_csrf into OpenAPI contract, admin-api password floor, bootstrap self-heal, review cleanups
Documentation
- readme: Capability map + module map, bilingual
- readme: Fix zh anchor to the actual SDK layering heading
- Documentation governance design v1 — triage of docs/ + Docusaurus content sourcing
- governance: V2 — content-level verdicts from full-text deep review
- governance: Phase A part 1 — contradiction fixes + triage execution
- governance: Phase A part 2 — six merges, 12 ADRs, audience navigation
- governance: Re-execute the history/locals untrack that git add -A had reverted
- governance: Phase B — Docusaurus site skeleton, zero-copy docs/ sourcing
- governance: User-only docs tree + IA-shaped restructure (review follow-up)
- Pre-launch content quality pass (audit P0/P1 fixes)
- api-reference: Unified simplified Chinese + OpenAPI governance flow
- adr: Bring the three governance trust archives into the repo
- Repoint root-facing doc links to the new IA + link fulla.dev
- Phase C deep-dives + English-primary bilingual site (en + zh-CN)
- Bilingual follow-up — zh README links to the zh-CN site, dual-write rule in CONTRIBUTING, changelog entry
- P2 cleanup — ship the verification script, linkify ADR references
- reports: Add runtime verification for Docker port binding (#112)
- repo: Add nested AGENTS.md guides for libs/drogon, libs/storage-postgres, apps/server
- deploy: Fix bootstrap flow, prod seed steps, TLS cert order
Testing
- Rerun-safe DB seeds in integration tests (#72)
- Event-driven Redis cache-fill waits replace fixed sleeps (#90)
- admin: LastAdminGuard self-heals its sole-admin premise (#72)
- admin: LastAdminGuard sweep must DELETE, not repoint (#72 follow-up)
- naming: Rename consent gate tests to convention; clean probe
CI/Build
- frontend: Add production builds to the fast frontend gate
- frontend: Add production builds to the fast frontend gate
- SHA-pin all workflow actions + dependabot.yml (#107)
- Sanitizer gates (ASan+UBSan PR leg, nightly TSan) + UBSan support (#104)
- frontend: Strict tsc, lint gate, e2e in CI — closing the frontend quality gap (#108)
- sanitizer: Prepend drogon_ctl's Conan bin dir to PATH (port of the coverage fix)
- deps: Bump anchore/sbom-action/download-syft
- deps: Bump actions/setup-go from 5.6.0 to 7.0.0
- deps: Bump actions/upload-pages-artifact from 3.0.1 to 5.0.0
- deps: Bump sigstore/cosign-installer from 3.9.2 to 4.1.2
- deps: Bump actions/cache from 4.3.0 to 6.1.0
Miscellaneous
- community: Issue templates (bug / feature forms) + contact links
- deploy: Loopback-bind dev compose data ports + credential warnings (#112)
- sdk: Regenerate python/go clients from the #71 link-state spec
- deps-admin: Bump the admin-frontend-minor group
- deps-user: Bump the user-frontend-minor group
- deps-admin: Bump pinia from 3.0.4 to 4.0.3 in /frontends/admin
- deps-admin: Bump globals in /frontends/admin
- deps-user: Bump pinia from 2.3.1 to 4.0.3 in /frontends/user
- deps-user: Bump globals in /frontends/user
- deps-admin: Bump eslint-plugin-vue in /frontends/admin
- deps-user: Bump vue-tsc from 2.2.12 to 3.3.11 in /frontends/user
- api-diff: Ratify WeChat env override additions (follow-up to #139)
- gates: Ratify additive api-diff (RuleSet policy + AuthService legacy gate); oasdiff errata for register password minLength
- gates: Oasdiff errata entry on one line for text matching
- Untrack local qoder diff artifacts (not for the repo)
- Ignore qoder diff artifacts
- gates: Oasdiff errata — consent_csrf required param is the security fix itself
Other
- Create CNAME
- Professional landing page, brand theme, Chinese locale, local search
Verification
Images are signed with cosign (keyless, GitHub OIDC):
cosign verify ghcr.io/voidvec/fulla-backend:1.0.1 \
--certificate-identity-regexp 'https://github.com/voidvec/fulla/.github/workflows/release.yml.*' \
--certificate-oidc-issuer https://token.actions.githubusercontent.comSPDX SBOMs for the three images and the source tree are attached,
as is the SDK tarball checksum:
sha256sum -c fulla-sdk-1.0.1-linux-x86_64.tar.gz.sha256Fulla v1.0.0
[1.0.0] - 2026-08-26
Series reset note: the project was renamed from authforge to fulla
(Fulla is the keeper of Frigg's secret coffer in Norse mythology), and the
version series resets to 1.0.0 with this release — the rename is
treated as a new product identity; SemVer constrains package identities,
not the repository (the PyPI packagefulla-oauth2, the
ghcr.io/voidvec/fulla-*images, and the CMake packagefullaare all
new identities). Prior history is preserved in the repository CHANGELOG
(the authforge-era [1.0.0]–[1.4.1] entries) and in the full git history.
Rename & Branding
- Repository-wide rename authforge → fulla: C++ namespaces and public
header paths (#include <fulla/...>), CMake packages and targets
(fulla::*), binaries (fulla-server/fulla-tests), the Go module
path, the Helm chart, and the benchmark facility. The api-diff baseline
was regenerated for the new symbol surface (179 headers, ratified with
--force, PR #94). - Environment variable prefix unified to
FULLA_*(previously
OAUTH2_*, 903 occurrences; protocol class names such asOAuth2Plugin
are unchanged). - Infrastructure naming normalized: database/role
fulla_db/
fulla_user, container namesfulla-*, Redis key prefixfulla:cache:,
Prometheus metricsfulla_*(upgrading invalidates the whole cache at
once — expected one-time behavior). - Frontend branding: Fulla Admin / Fulla, package names
fulla-admin/
fulla-user. - Repository governance professionalized (PR #93): AI-tool workspaces
untracked, kiro design docs moved todocs/history/design/kiro-specs/,
.claude/is the single authoritative rules source.
Compatibility
- C++ SDK consumers: breaking (include paths, namespaces, and the CMake
package name all changed); migration is essentially one sed pass (mapping
table indocs/adr/rename-impact-fulla.md§3). - Existing JWTs / sessions / database schema: fully compatible (the
protocol surface carries no project name; table names and migrations are
unchanged). - The Python SDK package is renamed to
fulla-oauth2; the old
authforge-oauth2is discontinued.
Verification
Images are signed with cosign (keyless, GitHub OIDC):
cosign verify ghcr.io/voidvec/fulla-backend:1.0.0 \
--certificate-identity-regexp 'https://github.com/voidvec/fulla/.github/workflows/release.yml.*' \
--certificate-oidc-issuer https://token.actions.githubusercontent.comSPDX SBOMs for the three images and the source tree are attached,
as is the SDK tarball checksum:
sha256sum -c fulla-sdk-1.0.0-linux-x86_64.tar.gz.sha256