Skip to content

Releases: swina/whoobe_project

Whoobe v0.3.3

Choose a tag to compare

@github-actions github-actions released this 27 Oct 14:08
v0.3.3

Whoobe 0.3.3 - release notes

A patch release of 0.3.0 (Operate): the tokens used by the AI functions, shown to whoever asked and, when an administrator turns it on,
recorded per person; a retention period for the AI history. Written from CHANGES.md on 2026-10-27; the procedure is in
release-process.md. One migration (0073_ai_usage.sql).

AI usage

  • Every AI answer carries what it consumed. The answer of each AI function (page, snippet and element text, website structure, wizard
    texts, image, page translation) has a usage object: provider calls, tokens in, tokens out. The dashboard and the editor show it in a small
    notice to the person who asked. Nothing is stored for this. Retries and translation batches are counted; a reply that fails to parse is
    counted (it was paid for); a provider that reports no tokens counts as a call.
  • Recording per person, off by default (Security > AI Usage, settings:manage). While on, one row per request: the person, the
    function, provider, model, tokens and date, never the prompt, the answer or what was being edited, deleted after 90 days by default;
    monthly totals stay until the person is deleted. The page carries a warning: a record per person is personal data about staff and can be
    used to watch their work; inform them, and check whether your country needs an agreement for such a tool (in Italy, article 4 of the
    Statuto dei Lavoratori). See data protection.
  • The report opens as its own tab (Open the AI usage report): totals by person, function, model and day, one person's requests, monthly
    totals, CSV export. Every user's figures need the new permission ai_usage:read (administrators only by default); opening one person's
    requests is written to the audit log. Every signed-in person sees what is recorded about them under Mine.
  • Providers: OpenAI-compatible APIs, Anthropic and Google report tokens; for images, the gpt-image models and OpenRouter (tried with a real
    OpenRouter image model). Details in AI features, section 5.

Security

F-064 (security/findings.md, Medium), found by the project on 2026-10-27 while designing the report: the AI history
(whoobe_ai_generated) keeps the full prompt and answer of every page, snippet, translation and image generation with the user id, without
any retention, and was missing from the personal-data table of the data-protection page. Affected: every earlier release. Fixed: a period
for it can be set next to the AI usage switch (Keep generated pages and prompts for), applied by the daily retention job; the data-protection
page lists both tables. Empty keeps them, as before: set a period if you do not need the history.

Other changes

  • Data Objects: the Generate button of a slug field, on an empty field, now makes the slug from the object's title (it did nothing before).
  • Documentation: How Whoobe evolves, what comes next, what is built only when an organisation funds it, and
    the rule that the code stays MIT; contributors and testers are asked for.

Behaviour changes to check before you upgrade

Change What you see What to do
Migration 0073_ai_usage.sql: tables ai_usage and ai_usage_monthly, three columns in security_settings runs at the start of core-api back up before upgrading, as for any migration
The answers of the AI routes have a new usage field nothing, unless a client of yours rejects unknown fields allow the field
New permission ai_usage:read, given to the admin role administrators see every user's AI usage report give it to another role only if that role should see it
AI usage recording off: nothing is recorded until an administrator turns it on inform your staff before turning it on
Retention of the AI history unchanged (kept) until you set a period set one in Security > AI Usage if you do not need the history
New routes GET /ai/usage, /ai/usage/monthly, /ai/usage/me in the API reference none

No new environment variable. Upgrade as for any release (scripts/workspaces.sh upgrade v0.3.3, or pull the 0.3.3 images).

Known limits

The limits of 0.3.0, 0.3.1 and
0.3.2 still hold. For this release: the token counts are those the provider reports, not a bill (no
price is computed); a provider that reports no tokens shows calls only; the recording covers the dashboard and editor AI functions, not the
publisher or the Gateway's API clients. Tried against a real provider (OpenRouter): images, a snippet and the text of an element; the
website wizard and the page translation share the same provider code and were tested with a fake provider only.

Images (by digest, signed with cosign, keyless)

admin-dashboard ghcr.io/swina/whoobe-admin-dashboard@sha256:c540e788fb73dd9068d56db56d71deaa497fcdeb1742558e2f53763b932f3c26
asset-processor ghcr.io/swina/whoobe-asset-processor@sha256:7d9a2909c40e66560cd98ac55423c24af35a6a2a902f87c93fdc1dba71d48657
core-api ghcr.io/swina/whoobe-core-api@sha256:87792af53a7cb218c9a525f20706bb5d8ab9881a82187dd81f99bc49acc03aa7
gateway ghcr.io/swina/whoobe-gateway@sha256:698ba5971eb51bee5d01611bc577de07ea926678512eab2dd8e116f7b8e83bb3
publisher ghcr.io/swina/whoobe-publisher@sha256:ccb58a9ccfdd9f9af29865a8bfa27b999a4676a6f90c2650ce1e04634cb9a656
whoobe-one-spike ghcr.io/swina/whoobe-whoobe-one-spike@sha256:497ad275ec39c1ca434f16a7987fa8200816345095e220c3fadfebf42f3ebfb8

Whoobe v0.3.2

Choose a tag to compare

@github-actions github-actions released this 26 Oct 23:03
v0.3.2

Whoobe 0.3.2 - release notes

A patch release of 0.3.0 (Operate): the publisher image without the build tools it carried, and the image scan of the CI now covering it. Written
from CHANGES.md on 2026-10-26; the procedure is in release-process.md. No migration.

Security

F-063 (security/findings.md), found by the project on 2026-10-26 while checking the published 0.3.1 images. The
publisher image (the optional service that runs the site generator for the dashboard's Publish tab) was never in the image scan of the
security workflow: the scan took its list from docker compose config --images, and the publisher sits behind the compose profile publisher.
The published whoobe-publisher:0.3.1 has 171 fixable HIGH/CRITICAL reports (11 critical), almost all in build tools shipped with it: the Go
standard library of three esbuild binaries, the pnpm 9.0.0 left in the corepack cache, the npm of the node base image, and dev packages of
the build tree (vite, vitest, tinypool, devalue, brace-expansion). Affected: the publisher image of 0.3.1; earlier releases were
built the same way and were not measured. The other five images are not affected.

Exposure: the publisher is optional, publishes no port and runs only the site builds the dashboard asks for; none of these tools runs as a server,
but they do run on content written by editors.

What changed:

  • the publisher is built like the other services: the runtime base without pnpm, corepack or npm, and the pruned production tree. 0 fixable
    HIGH/CRITICAL on the new image;
  • the SDK packages the generator puts in each generated site (vendor/*.tgz) are packed when the image is built, so the image needs no package
    manager; outside the image the generator still packs them with pnpm;
  • the image scan of the security workflow builds and scans the publisher too, so a release cannot again ship it unscanned.

Behaviour changes to check before you upgrade

Change What you see What to do
The publisher image has no pnpm, npm or corepack the same generated sites; the SDK tarballs in vendor/ come from the image a script of yours that ran a package manager inside the publisher container: run it elsewhere
New environment variable WHOOBE_SDK_TARBALL_DIR, set in the image to /opt/whoobe-sdk (configuration reference) nothing do not override it in your compose file
Documentation sites: whoobe-publish --kind docs has three new optional flags, --version-label, --version-url and --repo-url (a version badge after the site title, a repository link in the header) nothing until you pass them see documentation sites

No migration. Upgrade as for any release (scripts/workspaces.sh upgrade v0.3.2, or pull the 0.3.2 images). Only the publisher image
changes in substance; pull or rebuild it if you run the publisher profile.

Known limits

The limits of 0.3.0 and 0.3.1 still hold. For this
release: a publish job was run in the new image through the publisher's own job runner (Vue and Next sites, against a local API), not from the
dashboard of a running workspace with the publisher profile; the arm64 image is built and scanned only by the release workflow.

Images (by digest, signed with cosign, keyless)

admin-dashboard ghcr.io/swina/whoobe-admin-dashboard@sha256:8e79a923108fd445156778472cb610116615d42e7ff7c6a53ecc59fd64e13e15
asset-processor ghcr.io/swina/whoobe-asset-processor@sha256:bb53311c7d6d8fbfac89a4e3aae2120055b414640e621c67fb9da2633b557136
core-api ghcr.io/swina/whoobe-core-api@sha256:68a0ad91424dfa22b1a457ca8f20f3665910d87000c4981eb3d61fccf82bb70d
gateway ghcr.io/swina/whoobe-gateway@sha256:1c94b267cb83e8803ee2174468975729946afe6c340cab8b646581ed426e19f0
publisher ghcr.io/swina/whoobe-publisher@sha256:1a89eb22d2c7b14ab09dd0df7c10e81f12d667204ed694797cd8fa09c0e4e727
whoobe-one-spike ghcr.io/swina/whoobe-whoobe-one-spike@sha256:643908abe9277cff3b0197d387451333bc9c55e7dad51e2b0bef782aa1e11d88

Whoobe v0.3.1

Choose a tag to compare

@github-actions github-actions released this 26 Oct 18:32
v0.3.1

Whoobe 0.3.1 - release notes

A patch release of 0.3.0 (Operate): security updates of dependencies and of the two nginx images, and two changes to the public sandbox's
visit log. Written from CHANGES.md on 2026-10-26; the procedure is in release-process.md. No new feature, no migration.

Security

Advisories published by others in third-party packages, found by the nightly security run of 2026-10-24 (static scan, pnpm audit --prod) and
by the image scan; none was found by the project, so all are public (disclosure policy). Affected: every release
from 0.1.0 to 0.3.0
(the same versions are in the lockfile of each tag).

No 0.2.1. The support policy (release process, section 2) gives security fixes to the latest two minor lines; for this
patch the owner decided (2026-10-26) not to release a 0.2.1: 0.2.0 is one week old and the upgrade from it to 0.3.x has six migrations, tested from
0.2.0 on 2026-10-23. On 0.2.x, upgrade to 0.3.1.

Package Was Now Advisory Where
zod 3.25.76 4.6.7 GHSA-mrhx-c7xc-m3w5 (high): memory exhaustion when an array schema without a length limit validates a large array core, schema SDK (validation of Data Objects written through the gateway, site specifications, page content)
body-parser 1.20.6 1.20.9 GHSA-27r3-jxmh-p828 (high): denial of service via trailing bytes after a compressed body express in core-api, asset-processor, gateway
multer 2.4.0 2.5.0 GHSA-q7qp-rjg7-q26m (high): denial of service via a pending file filter after the request is torn down asset-processor uploads
undici 6.28.1 6.29.1 GHSA-xjj6-f33q-929c (high): denial of service via too many content encodings gateway (OpenAPI parser)
brotli-libs 1.2.0-r1 1.2.1-r0 CVE-2026-111497 (high) the admin-dashboard and whoobe-one-spike images (nginx alpine base)
curl, libcurl 8.22.0-r0 8.23.0-r0 CVE-2026-92392 (high) the same two images

The two nginx images now upgrade every Alpine package at build time instead of a list of named ones (the list grew by one package each
time the pinned base fell behind). The register entries are F-003 (dependencies) and F-050 (images) in security/findings.md. postgres:16-alpine still carries
the 26 reports of its gosu helper (advisory, see 0.3.0).

Behaviour changes to check before you upgrade

Change What you see What to do
Zod 4 validates Data Objects written by API clients, site specifications and page content the same values pass and fail; some error messages read differently (a site specification's messages are still rewritten in plain words) a client that matches the exact text of an error message: check it
The JSON Schema of a site specification (Settings > Import site specification > Download, GET of the spec endpoint) describes the element union with oneOf instead of anyOf same meaning (each element has its own kind) a tool that does not accept oneOf: tell us
Sandbox profile: requests from ClaudeBot are not written to the visit log; new optional SANDBOX_UNTRACKED_IPS in /etc/whoobe-sandbox.env (addresses or CIDR ranges left out of the log, see sandbox) a smaller log after the next reset, sudo nginx -t && sudo systemctl reload nginx once

No migration. Upgrade as for any release (scripts/workspaces.sh upgrade v0.3.1, or pull the 0.3.1 images); the images must be rebuilt or pulled,
because the fixes are inside them.

Known limits

The limits of 0.3.0 still hold. For this release: the API and browser suites, isolation, TLS and
DAST passed on the release candidate 99365c4 (run 40297447238; the image scan PASS for five of the six own images: the publisher image is not in that scan, and the published 0.3.1 publisher has 171 fixable
HIGH/CRITICAL, mostly in build tools; F-063, found after the tag, fixed in a later patch), and the upgrade test from
v0.3.0 passed on 99c5041 (run 40295030726; only the Dockerfile of the two nginx images changed after it); a manual try of the site wizard, the spec import and gateway writes with Zod 4 on a running stack
was not done.

Images (by digest, signed with cosign, keyless)

admin-dashboard ghcr.io/swina/whoobe-admin-dashboard@sha256:019ba2ae4455c519db90a853565707247c5ace666a8e65ce03cccff7dec7ebca
asset-processor ghcr.io/swina/whoobe-asset-processor@sha256:1e9056d17e5394af84ae075c8113f9f04c91cecaf78af303714f352c2fdd1dfe
core-api ghcr.io/swina/whoobe-core-api@sha256:ace6e0b921ca7817a039dbf0c79d02740675fa8ddb061a918b981232e7fd204a
gateway ghcr.io/swina/whoobe-gateway@sha256:e94af59dcb39796eb45e1cc61b5196dc735d220278cc233536de4446d5f9adb0
publisher ghcr.io/swina/whoobe-publisher@sha256:a314e8d8282fa8a7df7f25092859af8256b8ec19d31e489e7f953087a2b14899
whoobe-one-spike ghcr.io/swina/whoobe-whoobe-one-spike@sha256:a7bc99daa60cdc866f33af7f642f4fdf1bc5b7b114326f4ec73bf47f46c46ef2

Whoobe v0.3.0

Choose a tag to compare

@github-actions github-actions released this 23 Oct 13:23
v0.3.0

Whoobe 0.3.0 - release notes

Release 0.3.0, named Operate: Whoobe for those who run it for others (an agency with many client workspaces) and for an organisation on a
private network. Written from CHANGES.md on 2026-10-23; the procedure that turns this into a release is in release-process.md.
The plan is docs/plans/system/release-0.3-plan.md; on 2026-10-23 its scope was cut (decision 7): what is below is built, the rest moves to 0.4
(Not in this release yet). Because the version starts with 0., a minor release may change behaviour: read Behaviour changes before upgrading.
Base image: Node.js 22.23.4.

What is in it

Operate many workspaces

  • Fleet commands, scripts/workspaces.sh: list, status, verify, backup and upgrade <tag> [--canary slug] [--no-backup] [--yes] over
    every workspace of the host. One failing workspace does not stop the others (exit status 1, the slugs are listed). upgrade backs up first (a
    workspace whose backup fails is not upgraded), checks the tag out once, waits for health, and on a workspace running released images puts back the
    previous WHOOBE_VERSION if the new images cannot start; if no workspace could be recreated, the tree goes back to where it was. Every action is a
    JSON line in $HOME/.whoobe/fleet-actions.log. Tried on three real throwaway workspaces and on released-style images. Guide.
  • Workspace label. WHOOBE_WORKSPACE adds workspace="<name>" to every core-api metric and a workspace field to every log line (unset: output
    unchanged).
  • Install from released images. provision-workspace.sh --image-prefix P --image-version V starts a workspace from published images with no build;
    the dashboard and the editor read their addresses when the container starts (/config.js), so one image serves every workspace.

Private network (no route to the internet)

  • Profile private (provision-workspace.sh --profile private): no Google Fonts, no Material Icons CDN, no Unsplash or picsum; Inter is bundled in
    the dashboard and the editor, and image placeholders are a local grey picture. Run on a Docker network with no route out: 7 containers healthy, the
    API suite and workspaces.sh verify pass, no failed outside connection in any log. Page.
  • Custom fonts. Upload woff2 files in Settings > Custom fonts (several files of a family at once; family and style guessed from the names).
    They are used in the editor, copied into generated sites and mobile apps, and written as @font-face in e-mail. Tried end to end on a network with no
    route out. Settings guide.
  • enable-tls.sh --reuse-cert accepts a certificate from the organisation's own CA on a private host name (syntax checked, not run).

Generate a site

  • A site specification any LLM can write: schema, expansion into a bundle, instructions, check and import.
  • Template wizard and Generate Website Wizard: snippets as default blocks, with placeholders; texts written by an AI (needs a configured provider;
    the AI provider path has not been exercised); palettes made by a person, with a Palettes section in the menu; pages reorderable and collapsible.
  • Kits: a section of the dashboard to manage, export as zip and import; preview of the templates, Duplicate to edit, origin and version.
  • Website preview with draft pages; deleting a website deletes its pages, templates, paths and theme; where a snippet is used, with a filter by
    website or template.
  • Pages: a copy link button opens a page in the editor for a colleague on the same network (no credential in the link).
  • Published sites: fixes to links, trailing slashes, static redirects, covers of the blog, the http to https redirect of the generated vhost, and the
    classes of a container.

Supply chain

  • release.yml requires an annotated tag whose signature GitHub verifies, builds six images (core-api, asset-processor, gateway, publisher,
    admin-dashboard, whoobe-one-spike) for amd64 and arm64, signs them with cosign, attests build provenance (SLSA) and drafts the release with the
    digests, openapi.yaml and the SBOM. Run on a trial tag, then installed from ghcr.io on an isolated network; the trial was removed.

Security policy

  • Disclosure policy: a High or Critical finding found by the project is private until the release that fixes it, then public in the register
    (new column Public from); the private place is a draft security advisory with its temporary private fork; acknowledgement in 7 days, target 30
    days to fix High/Critical and 90 days Medium. Policy, SECURITY.md.
  • Security results stay public with a release gate; markdown-it 14.3.3 and libx11 updated in the images.

Behaviour changes to check before you upgrade

Change What you see What to do
The dashboard's image fallback was an Unsplash photo; it is now a local grey picture broken images in the dashboard look different nothing
docker-compose.yml names the five application images ${WHOOBE_IMAGE_PREFIX:-whoobe}-<service>:${WHOOBE_VERSION:-local} unchanged when the variables are not set nothing
The dashboard and the editor read /config.js at start (build-time values remain the fallback) none for a workspace built as before rebuild keeps working
nginx routes /fonts, /whoobe-one/editor-base, and a browser navigation to /settings, /pages, /documents reaches the dashboard a refresh of these pages no longer shows Cannot GET on a workspace provisioned earlier, run scripts/render-workspace-nginx.sh <slug> and reload nginx
POST /fonts and wizard palette create/update answer 409 (not 500) on a duplicate nothing
Excel import and XLSX export removed (the library xlsx had two high advisories and no fix): Objects > Export offers CSV and JSON; Class from file accepts .json, .csv, .tsv a .xlsx, .xls or .ods file is refused save the sheet as CSV first
Kits get an origin: every kit that exists is marked user (editable); bundled kits are read-only, refreshed by the seed, and copied with Duplicate to edit. Kit templates with a duplicate or non-string id get a new one (migration 0068) the Kits section shows origin and version nothing
The dashboard and the editor no longer load Google Fonts or Material Icons: Inter is bundled the dashboard looks the same, with no request to fonts.googleapis.com nothing
New optional variables: COMMUNITY_URL (the Help entry of the dashboard; unset = the project's GitHub Discussions, empty hides it), ADMIN_PUBLIC_URL (also used by copy editor link), PLACEHOLDER_IMAGES / VITE_PLACEHOLDER_IMAGES (local = grey picture instead of picsum), WHOOBE_WORKSPACE (label in metrics and logs), WHOOBE_IMAGE_PREFIX / WHOOBE_VERSION (named images; set by provision-workspace.sh --image-version) the dashboard sidebar has a Help entry set COMMUNITY_URL= to hide it; all others are off by default
In Assets, a type filter looks in the whole library and hides folders; audio files (mp3, wav, m4a) have their own type and player; files already stored as application/octet-stream are served with the type of their extension an mp3 uploaded before opens in an audio player nothing

Six migrations (0067 to 0072), applied by core-api at the first start; back up first (scripts/workspaces.sh upgrade does). Five are additions (new columns on kits and snippets, tables wizard_palettes and custom_fonts); 0068 rewrites data: in each kit, template ids that are not a string or repeat an earlier one are replaced by tpl-<8 hex> (the old editor wrote the number 0 for several templates); kits whose ids are fine are not touched. Upgrade test from v0.2.0 on 2026-10-23: 67 migrations at the start, 73 after, data written by 0.2.0 intact, login works (CI run 39937017622).

Known limits of this release

  • The Assets changes (the type filter across folders, audio types and player, the PDF preview sending the session) were not checked in a browser before
    the release; the PDF preview 404 reported by the owner has a probable cause (the session cookie missing on a cross-origin fetch) that was not reproduced.
  • The Generate Website Wizard has never been run with a real AI provider; the manual security checklist was not repeated for the features added since 0.2.0
    (see the status summary).
  • postgres:16-alpine carries 26 HIGH/CRITICAL reports, all in its gosu helper (Go stdlib, runs once at start); reported, not gating. A derived image
    without it is planned for 0.4.

Not in this release yet

Level 1 items of the plan that are not built, and so not promised: more than one replica (challenges and counters out of memory, shared limiters,
migration lock, multi-replica test), the managed-database variant and Helm chart, failed-attempt audit with correlation id and hash chain, metrics for
asset-processor and gateway, rehearsals on the reference host, the enterprise profile and the installation checklist. Not tried: pnpm build of a
generated site on a private network (needs a registry mirror), a real e-mail send, the mobile-app and docs site kinds, real Cloudflare Images.

Images (by digest, signed with cosign, keyless)

admin-dashboard ghcr.io/swina/whoobe-admin-dashboard@sha256:fd20431717aeeaeb72c1d73e7de626e3ce8d25ab9dfddc3041904f83cbd39227
asset-processor ghcr.io/swina/whoobe-asset-processor@sha256:0496536aa3773044207b3434f249d297abacdb459f4f3dfd9549a51726fe5f37
core-api ghcr.io/swina/whoobe-core-api@sha256:a0bd2986d728e73ae60832940d9ff18cb4c448dd44d2fa2ef8705efe4478abc6
gateway ghcr.io/swina/whoobe-gat...
Read more

Whoobe v0.2.0

Choose a tag to compare

@github-actions github-actions released this 19 Oct 21:16

Whoobe 0.2.0 - release notes

Release 0.2.0, named Conduct: the release that lets an organisation with a security officer say yes. Written from CHANGES.md on 2026-10-19;
the procedure that turns this into a release is in release-process.md (copy the text into .release/notes-v0.2.0.md after
scripts/release.sh prepare). The plan it follows is docs/plans/system/release-0.2-plan.md. Because the version starts with 0., a minor release may
change behaviour: read Behaviour changes before upgrading. Seven migrations (0060 to 0066), all additions; back up first.

What is in it

Govern: what a security officer asks

  • Audit log of administrative actions. An append-only table (the database refuses UPDATE and TRUNCATE; DELETE only for the retention job) with
    who, from which address, what, on which target and the fields before and after; secrets are recorded as "changed", never as a value. Users, roles,
    websites, settings, publication, integrations, single sign-on, data subject requests. A route that changes something must say whether it is
    audited: a test fails for a new route with no decision. Read it in Security > Audit Logs (filters, CSV), GET /audit-log, permission
    audit:read. Retention AUDIT_RETENTION_DAYS (365). Guide.
  • Mandatory two-factor authentication and a password policy. An administrator chooses the roles that must use a second factor; a user of such a
    role who has none is led to enrolment at the next sign-in and gets no session until it is done, and cannot turn it off afterwards. The password
    policy (minimum length 8 to 128, optionally three of four character classes, never the e-mail address) applies where a password is set. Off by
    default: an upgrade changes nothing.
  • Single sign-on for staff (OpenID Connect). Authorization code with PKCE against one provider (Keycloak, Microsoft Entra, Okta, Google ...), roles
    from a claim at every sign-in (the roles in the mapping follow the claim, the ones given by hand are left alone), provisioning and linking by a
    verified e-mail only, localLogin for the break-glass of administrators. A person removed at the provider cannot start a new session. Tried
    against the test mock and a real Keycloak 26; Entra, Okta and Google follow the standard and were not tried. SAML and LDAP are not built (a
    broker in front covers them). Guide.
  • Data subject requests and retention. Find, export and erase everything held about one person by one value (an e-mail): form submissions, the
    files they uploaded (deleted from the library first), the mail log; staff accounts are reported, never erased by it. A dry run first, the value typed
    again to erase, audited with a fingerprint and never the value. Retention per data class and for the mail log, deleted daily, off until you set
    a number
    . Procedure.
  • Readiness and metrics. GET /health answers 503 when the database does not answer; GET /health/live is the cheap liveness; GET /metrics
    (Prometheus: requests by matched route, queue depth of publication jobs and event deliveries, database up, audit rows lost) is off unless
    METRICS_TOKEN is set. Observability.

Orchestrate

  • More events. Files in the library (asset.created, .updated, .moved, .deleted, .bulk) and pages (page.published, .unpublished)
    through the same subscriptions, delivery, signature and loop guard as object events. The envelopes carry ids and metadata, never contents.
    Object events.

CDN mirroring

  • Per website, with a default, and a second provider: Cloudflare Images next to Cloudinary (it takes multipart/form-data, which the Gateway
    cannot send, so Whoobe calls it directly with an API token stored encrypted). The mirror of the website's own provider is served when an asset has
    several. Tried against a mock of Cloudflare Images only: a real account has not been tried. Publishing, step 1b.

Dashboard

  • A Security entry in the sidebar groups Roles/Users/Workspaces, Two-Factor Authentication, Sign-in Requirements, SSO, Data Subject Requests, System
    Logs and Audit Logs; each page is shown by permission. The sidebar opens as four closed groups with closed submenus (showing a section opens its own).
  • Websites > Paths: a pencil on a path linked to a page opens it in the editor. The status badge reads /health and the build version.
  • A Resend mail provider recipe (the sender goes out as the address only).

Operations and quality

  • The upgrade test, the restore rehearsal, the load test and the release workflow of 0.1.1 stay; the upgrade test now runs from 0.1.1 to this tree
    across the seven new migrations.
  • API suites of the new features run on the disposable stack with a mock OIDC provider that forges nine kinds of token, a mock Cloudflare Images,
    a real Keycloak (opt-in) and real form uploads; check-docs.py is green again (earlier branches had edited the generated API reference by hand).

Behaviour changes to check before you upgrade

Change What you see What to do
Migrations 0060 to 0066: audit_log (append-only by trigger), security_settings, oidc_settings, user_identities, cdn_mirror_settings; columns data_classes.retention_days, security_settings.mail_log_retention_days, oidc_settings.dashboard_url, object_events.subject_type; object_events.class_id becomes nullable; three indexes applied by core-api at the first start; 0066 copies an existing CDN mirror setting (kept until now in the first website's settings) as the default back up first (upgrade); going back to 0.1.1 means restoring that backup (the migrations are additions, a rollback on the migrated database is not tested)
New permissions audit:read and data_subject:manage, given to the role admin at the first start Security > Audit Logs and Data Subject Requests appear for administrators grant them to other roles if wanted
PUT / DELETE /whoobe-one/settings/cdn-mirror need settings:manage (they needed objects:write) an editor can no longer change the CDN settings (reading stays) grant settings:manage or let an administrator do it
GET /health answers 503 when the database is down; the Docker HEALTHCHECK of core-api calls /health/live a load balancer probing /health takes the instance out during a database outage point liveness probes at /health/live, readiness probes at /health
nginx: new core-api prefixes (audit-log, security-settings, oidc-settings, data-subject, and the ones 0.1.1 missed: mail-providers, docs-sites, object-event-*, pwa, publish/jobs, api/v1/system) on a host nginx, a new screen fails with "Unexpected token '<'" until the include is re-rendered scripts/render-workspace-nginx.sh <workspace> after the upgrade; /metrics is deliberately not routed
Dashboard navigation: Roles/Users and Security left Settings; System Logs and Audit log are inside Security; sidebar groups and submenus start closed people look for them in the old places the dashboard tour and the settings guide describe the new ones
@whoobe-cms/core depends on jose; the token verifier of the gateway moved into core none none
Public sandbox: /security-settings, /oidc-settings and /data-subject are blocked the demo account cannot switch on mandatory 2FA or a provider none
New optional variables: AUDIT_RETENTION_DAYS, METRICS_TOKEN, OIDC_ALLOW_HTTP, OIDC_START_RATE_LIMIT_PER_MINUTE, DATA_SUBJECT_RATE_LIMIT_PER_MINUTE, CDN_ALLOW_HTTP, CDN_CLOUDFLARE_API_BASE none unless set configuration reference

Defaults that do not change on upgrade: nobody is required to use two-factor authentication, single sign-on is off, no retention is set, /metrics is
off, no password rule beyond the length of 8.

Known limits

  • Single sign-on: one provider, no SAML or LDAP, no single logout. A session already open outlives the person's removal at the provider (up to 7
    days) until an administrator deactivates the account. Entra, Okta and Google were not tried.
  • Data subject requests match the whole value only, find a person typed inside a longer text in the mail log but not in objects, and do not reach
    backups (a restore brings the person back: erase again). There is no register of requests.
  • Audit log: successful actions only; no failed attempts, no gateway administration, no tamper evidence beyond the database rule.
  • Events: creating, editing and deleting a page, and the end of a publish job, are not events yet.
  • CDN: a real Cloudflare Images account was not tried; deleting an asset does not delete its mirror.
  • In-memory state in core-api (the sign-in in progress, the enrolment tokens, the login throttle) is per process: one core-api replica.

Tested

The API and browser suites on the disposable stack, the unit and integration suites of core, check-docs.py, the upgrade test from v0.1.1, the
static scans, the isolation and TLS checks and the ZAP scan on the release commit: see security/status-summary.md.

Images (by digest, signed with cosign, keyless)

asset-processor ghcr.io/swina/whoobe-asset-processor@sha256:707e6ca70b32abd5bf1704d064563985cbf4f844db38b6821e4b5eb7d4de797b
core-api ghcr.io/swina/whoobe-core-api@sha256:db04ed0e2718ad4bfcb0c378bfe838600a35112d0c4776483c15b4e11418dc3a
gateway ghcr.io/swina/whoobe-gateway@sha256:825be06b36ac7ac012a8e1e1e8e663dda8...
Read more

Whoobe v0.1.1

Choose a tag to compare

@github-actions github-actions released this 18 Oct 18:06

Whoobe 0.1.1 - release notes

The first release after 0.1.0 (2026-10-14): the work done on master since then, 80 commits. One version number for the whole
product. Written from CHANGES.md on 2026-10-18; the procedure that turns this into a release is in
release-process.md (copy the text into .release/notes-v0.1.1.md after scripts/release.sh prepare).
Because the version starts with 0., a patch release is meant to contain fixes; this one also carries one new feature (object
events) that touches the database and the gateway, so read "Behaviour changes" before upgrading.

What is in it

Object events (new)

  • A subscription tells an automation engine or any HTTP service when a Data Object is created, changed, deleted, published or
    unpublished: outbox, delivery with retries, signature (HMAC), delivery log, test event. Dashboard section Data & Communication >
    Object events
    (permission object_events:manage), the Connect a flow wizard, the API (eight routes) and an administrators'
    guide: communication/object-events.md.
  • End of a flow and loop guard: a write through the client API with the header X-Whoobe-Flow: stop is recorded but not delivered
    (status stopped); a client that keeps writing the same record is cut by a circuit breaker (people are never cut). The delivery log
    is a tab with filters, selection and delete.
  • Whoobe does not ship or favour an automation engine: the guides cover Node-RED, Activepieces and n8n as the operator's choice
    (communication/node-red.md); the example flows are in examples/, not part of the distribution.

Dashboard

  • Objects list: compact rows, a spreadsheet-like table view (resizable columns, cells editable in place), Copy id in the row menu,
    Rows per page fixed from page 2 on; the tab of an open record is named by a field marked tab title. A click on a row no longer opens
    the record (right click > Edit). Guide: content/objects-list.md.
  • Object edit form: field classes override the defaults (Class > View Layout > Input styles), a Refresh button, markdown and rich-text
    editors follow a height set on the field.
  • The API clients list shows write scopes as write.

Generated sites and SDK

  • A markdown field bound to an element is converted to HTML, and a wysiwyg field kept as markup, both sanitised, like in the editor
    preview (@whoobe-cms/renderer; before, a generated site showed the source). Regenerate and republish a site to get it.

Operations and quality

  • Upgrade test: scripts/upgrade/upgrade-test.sh installs the previous release, writes data, upgrades to the working tree and checks the
    migrations, the tables, the data and a login; it also runs as a GitHub workflow on a release tag. Passed 0.1.0 -> this tree.
  • Restore rehearsal and bucket backup: scripts/backup-workspace-bucket.sh (copies the S3 bucket, or restores it) and
    scripts/restore/rehearse.sh (backs up a throwaway workspace, deletes it, restores it and checks the data).
  • Load test: scripts/load/run.sh (k6): about 690 public pages, 330 public queries and 120 API-client reads per second under the smallest
    container limits, on a development machine; see operations/capacity-and-scaling.md section 2.1.
  • Release workflow (.github/workflows/release.yml): signed multi-arch images on ghcr.io and a draft GitHub Release from a tag. Written,
    first run is this release.
  • Documentation corrected where it was no longer true (machine credentials exist as API clients; a form is not the only automation trigger; the
    editor preview and the published output are two implementations); the documentation is published at docs.whoobe.dev.

Security

  • The admin-dashboard and whoobe-one-spike images upgrade libexpat (HIGH CVE-2026-77214, fixed in 2.9.0-r0); all five product images pass the
    image scan with 0 fixable HIGH/CRITICAL.
  • tailwind-merge 3.7.0 and marked ^14.1.4 are new dependencies (MIT); THIRD_PARTY_NOTICES.md is current.
  • stack-tests, static scans, isolation, TLS and the ZAP DAST scan ran on the release commit: see
    security/status-summary.md.

Behaviour changes to check before you upgrade

Change What you see What to do
Migration 0059 adds three tables (object_events, object_event_subscriptions, object_event_deliveries); nothing existing changes applied by core-api at the first start back up first (upgrade); the previous version still runs on the new schema
The role admin gets the permission object_events:manage at the first start; other roles do not the Object events section appears for administrators only grant it to other roles if wanted
The gateway runs a delivery worker for object events, in the same env file as core-api inert until a subscription exists OBJECT_EVENTS_ENABLED=0 stops events and worker for the instance
New optional variables: OBJECT_EVENTS_ENABLED, OBJECT_EVENTS_CONCURRENCY, OBJECT_EVENTS_LOOP_MAX, OBJECT_EVENTS_LOOP_SUB_MAX, OBJECT_EVENTS_LOOP_WINDOW_SECONDS, OBJECT_EVENTS_SECRET_<NAME> none unless set configuration reference, .env.example
Markdown / rich-text fields bound to an element are converted in generated sites pages that showed raw markdown now show formatted text regenerate and republish the sites
Objects list: a click on a row no longer opens the record editors who relied on it right click > Edit
Images of the two SPAs are rebuilt with a newer libexpat none docker compose up -d --build
The product is called "Whoobe" (it was "Whoobe CMS"): login page, browser title, the issuer shown by an authenticator app for a new 2FA enrolment (existing enrolments keep working), the sender name suggested for mail, the CLI help text only; package names (@whoobe-cms/*), image names and the repository are unchanged nothing

Known limits

  • Everything listed as a limit in the 0.1.0 notes and in chapter 4 of the overview still applies,
    except that upgrading from 0.1.0 is now tested by script (database step only: image rebuild on a real host, nginx and the manual checks of step 7
    are written, not rehearsed) and a restore of a throwaway workspace is rehearsed.
  • No audit log of administrative actions, no OIDC login for staff, no mandatory 2FA: planned for 0.2 (plan).
  • The load figures come from one development machine and four endpoints; uploads, AI, publishing and mixed traffic were not measured.
  • release.yml has not run before this release: if it fails, the images are built by hand as in 0.1.0 (release process, steps 5 to 7).
  • The manual security checklist was not repeated for the features added after 2026-10-09 (object events included).

Images (by digest, signed with cosign, keyless)

asset-processor ghcr.io/swina/whoobe-asset-processor@sha256:017712e2051817ec21628346d5c4fb840d20f8c85a1385b9a6de6b19bc6ad50e
core-api ghcr.io/swina/whoobe-core-api@sha256:9d73650d4ec81938d916f3ea0596def6e3e0892ec2b9ea89da6fbf0a91db773d
gateway ghcr.io/swina/whoobe-gateway@sha256:ce2e1fd2033e123cf8c091b99f819305df8f881b1949c8ba20e8e93474decdf9
publisher ghcr.io/swina/whoobe-publisher@sha256:0b069fbd33a6a35a389071ed69e75c367f1e6a874f011136e8acef891d8c4273

Whoobe v0.1.0

Choose a tag to compare

@swina swina released this 15 Oct 04:42

Whoobe 0.1.0 - release notes

First release. One version number for the whole product (0.1.0: while the version starts with 0., a minor release
may change behaviour). Written from CHANGES.md and checked against the code on 2026-10-14; the procedure that
turns this into a release is in release-process.md (copy the text into
.release/notes-v0.1.0.md after scripts/release.sh prepare).

What is in it

Authoring

  • Page editor (Whoobe One): blocks, snippets and kits, themes, templates with header/aside/footer slots, dynamic website
    paths and menus, App Mode (phone frame with responsive classes), thumbnails from the Mobile/Tablet preview, version
    history, AI page generation and translation, Figma import.

  • Rich text with TipTap: colour, highlight, alignment, links, lists. The colours now survive selecting the paragraph and
    using another control.

  • Audio element (player or waveform, from an asset or a URL, optional title) and Alpine presets (named sets of
    directives applied from the editor). Guides: content/audio-element.md,
    content/alpine-presets.md.

  • Customize > Border sliders can give 1px (border), and their first step removes the class instead of setting a 0.

  • Website export and import as a .zip with everything the site refers to - snippets, module pages, data classes and
    objects, asset files - and the references rewritten on import, so the copy is a twin. Dry run, rollback on failure.
    Guide: content/website-bundle.md.

  • Documentation sites: a website whose source is a folder of Markdown assets, with an index (automatic or edited in the
    dashboard), navigation and client-side search, rendered by the editor app or generated as a static site. Guide:
    content/documentation-sites.md.

  • Documentation sites draw mermaid code blocks as diagrams (editor, dashboard preview and generated site; the library is copied into the site, not loaded from a CDN). Guide: content/documentation-sites.md.

  • Table element bound to data: a table can show a Data Class query or a gateway list, with chosen columns and formats, and paginate it
    (arrows or a More button); the pagination also works for gateway-bound repeaters. Also drawn by the generated Next.js, Nuxt and Svelte sites and the SDK (first page in the HTML; at most 1,000 rows). Guide:
    content/table-element.md.

  • Download element tied to an asset or a URL (link with download, or opened in a new tab); guide
    content/download-element.md.

  • Snippet lock: a locked snippet is not saved automatically.

  • Marketing automation: a form sends its submission to an automation engine (Activepieces, n8n or any webhook) through the Gateway, the
    engine can answer a one-word outcome, and the page reacts with AlpineJS directives to the new whoobe-form-result event. The forms
    that start an automation are listed in Data & Communication > Automations (and in the editor). Whoobe does not run flows
    itself. Guide: communication/marketing-automation.md.

  • File upload in forms: a File Upload field, per-website limits (Settings > Form uploads), content checks, private
    storage. Guide: communication/form-uploads.md.

  • Editor tools: a Data panel (the binding of the selected element, the fields of the nearest bound list as chips to click or drag, nested and linked bindings) and an AlpineJS panel (presets, with a built-in Form result preset that sets done, ok and outcome from whoobe-form-result, and the element's directives); a status bar with the selected element (type > <tag> > #id) and its Custom CSS (classes), a twin of Custom Classes in Advanced.

  • Forms: the message a visitor sees names the field that was refused (a field named differently from the class field is the usual cause); the form can be hidden once it has been sent (On success > Hide the form once it has been sent; editor preview and React, Vue, Svelte, Next.js and Nuxt sites). Guide: communication/marketing-automation.md.

  • A consistent look for buttons, fields and windows (checked for contrast), the editor's tools as an icon bar, a menu
    next to the collapsed navigation.

Data and assets

  • Creating classes faster: + From template (Contact, Category, Product), + From file (JSON, CSV, Excel: types are guessed, HTML becomes Rich text) and Settings > Classes > Integration import (from a GET route of an Integration); each can create the schema only or schema and data. Bulk import of objects (POST /data-objects/bulk: up to 500 rows per call, drafts, rows that break a rule come back in rejected).
  • Unique fields: a class field can be marked unique; a second object with the same value is refused (409 in the API, " is already in use" in forms).
  • Empty data: removes every object of a class after typing the class name (Data Objects list and Settings > Classes); logged.
  • Pimcore migration: class files with text ids and the Fields Summary of real installations are read correctly; before importing, Choose fields and import... lets the user pick and map fields; objects import in the background with a progress bar and a downloadable list of failed records.
  • Data classes, components and objects (Pimcore-style), with workflow and publication; DAM with folders, thumbnails,
    visibility rules, optional CDN mirror; Garage as the default S3 store for local/LAN, an external S3 in production.

Integration and publishing

  • Outgoing mail per website: SMTP or an HTTP mail provider described by a JSON file (OneSignal built in, more are a file each), secrets encrypted at rest. communication/mail-providers.md.
  • API Gateway with endpoints, routes, OpenAPI import, site keys, forms; vendor presets for the "Add Endpoint" picker
    (Brevo, Cloudinary, HubSpot, Mailchimp, Shopify, Stripe, a fake-products demo). A preset can now be added to a running
    gateway: drop the .json in apps/gateway/presets/ or in GATEWAY_PRESETS_PATH (a mounted volume) and reload the page.
  • Publishing a website as a Next.js or Nuxt project, a Capacitor mobile app, a headless SDK (React, Vue, Svelte).

Operations

  • Workspaces (one isolated stack per customer) with provision-workspace.sh, profiles and removal; TLS template.
  • Public sandbox: a workspace with a profile (deploy/profiles/sandbox.env) that turns AI, mail, imports, CDN mirror
    and outbound HTTP off, caps assets, and is rebuilt every three hours. operations/sandbox.md.

Security - 59 findings recorded, all High fixed; none of the review's findings is open (see below); API (1171 tests), unit (269), isolation (14), browser (30) and static suites,
OWASP ZAP and image/TLS scans run in CI on every release commit.
security/status-summary.md.

Behaviour changes to check before you install over a development setup

Change What you see What to do
Compose refuses to start without credentials; Postgres and the S3 store listen on 127.0.0.1 only docker compose names the missing variable; a database client on another machine cannot connect set POSTGRES_PASSWORD, S3_ACCESS_KEY_ID, S3_SECRET_ACCESS_KEY; WS_DATA_BIND_ADDRESS=0.0.0.0 to open the ports
Garage replaces MinIO as the default store a fresh stack has no MinIO; the MinIO overlay (its compose file and Docker build folder) is removed copy existing data to the new store (see operations/storage-backends.md); an existing MinIO can stay as an external S3 endpoint
Asset visibility is enforced (DAM_ENFORCE_ASSET_VISIBILITY, on by default); /public/** is public assets recorded as private answer 404 without a session move what public pages use under /public, or set the flag to false
Credentialed CORS only for an allowlist "Failed to fetch" from an origin that is not WHOOBE_HOSTNAME CORS_ALLOWED_HOSTS, CORS_ALLOWED_ORIGINS
Publishing needs objects:publish (pages, data objects, and now website import) 403 when an editor changes published; an imported site arrives as drafts grant the permission; ENFORCE_PUBLISH_PERMISSION=false restores the old behaviour
Rich text is filtered when rendered (no iframes/embeds, <style>, id, data-*; only colour, background and alignment as inline style) embedded HTML disappears from pages extend the allow-list in packages/sdk/schema/src/sanitize.ts
Image endpoints need a session or DAM_SERVICE_TOKEN; images over 100 MP are refused 401 from scripts, 413 on huge uploads DAM_SERVICE_TOKEN, ASSET_MAX_INPUT_PIXELS
Outbound requests (gateway, OpenAPI import, AI endpoints) refuse loopback and metadata addresses an endpoint on a local address is refused OUTBOUND_ALLOW_HOSTS, OUTBOUND_ALLOW_LOOPBACK; OUTBOUND_ALLOW_PRIVATE=false on internet-facing installs
Public routes are rate limited per address 429 for a server-side rendering host PUBLIC_*_RATE_LIMIT_PER_MINUTE; TRUST_PROXY=1 behind nginx
Gateway forms forward only the form's own fields an extra field is dropped save the page again and republish; GATEWAY_REQUIRE_FIELD_ALLOWLIST=true refuses old bindings
Password reset revokes sessions; reset tokens are stored hashed; MFA secrets are encrypted at rest tokens issued earlier stop working set TOTP_ENCRYPTION_KEY and keep it (losing it forces MFA re-enrolment)
5xx answers are generic in production no SQL or stack text in the...
Read more