Skip to content

Tale v0.5.34

Choose a tag to compare

@larryro larryro released this 18 Sep 10:53
217d97c

0.5.34 carries 2 merged pull requests. One gives the operator of a deployment a way to name who may create an organization, in place of the two extremes that existed: any signed-in user on a deployment you run yourself, and nobody at all — not even the owner — on a managed deployment, whose proxy refused organization creation for everyone. The other lets a person whose administrator set their password actually leave the forced-change wall on their first sign-in, instead of being bounced back onto it with the password already changed. No migration, no API contract change, no image in the stop-gated tier: the upgrade is tale update followed by a plain tale deploy.

Highlights

Who may create an organization is now the operator's to name (#3413)

Better Auth's organization plugin lets every signed-in user create an organization, and a managed deployment answered that with the opposite extreme: the CLI's proxy policy refused POST /api/auth/organization/create for everyone and answered the capability probe {"canCreate":false} without looking at who asked, so neither the owner nor the operator could open a second workspace, and nothing the deployment declared could change it.

TALE_ORGANIZATION_CREATORS names the sign-in addresses that may create an organization — commas, semicolons or spaces, matched case-insensitively, the same grammar TALE_DEPLOYMENT_CONFIG_ADMINS uses, through one shared parser. The backend judges every caller against it, in Better Auth's before-hook for the create route: the edge cannot know who is asking, and backend-api is reachable from the sandbox network the edge never sees — the same argument 0.5.33 made for accounts.

  • Unset keeps the previous behaviour: any signed-in user may create. A workspace deployment or an own Compose file that never heard of the variable is unchanged.
  • Set, a listed address passes without a database read; anyone else is refused with 403 ORGANIZATION_CREATION_FORBIDDEN once the deployment holds an organization, and the refusal is logged.
  • The first organization is always allowed — the setup flow's, or the managed bootstrap's — so setup is unaffected by any list.
  • A set but empty value closes creation to everyone after the first organization.

GET /api/app/organizations/capabilities answers the same predicate per caller, so the app shows Create organization exactly when the door would open; the create page's forbidden copy already existed in English, German and French, and the app needed no change.

A managed deployment declares the list instead of inheriting the blanket refusal. The deployment specification takes organizations.creators: 1–64 distinct addresses, literal or environment references that preparation resolves; two spellings of one address are refused as a duplicate. Declared, the CLI writes TALE_ORGANIZATION_CREATORS into the runtime environment as a managed variable (an environment entry cannot set it), leaves the two organization rules out of the proxy policy — the sign-up refusal stays — records the declaration in the runtime bundle so the bundle reader checks the policy against it, and includes the list in the receipt's input hash only when declared, so every earlier receipt keeps its hash. Undeclared, nothing changes: the edge refuses everyone as before, and the variable is removed if an earlier declaration wrote it.

The rule is documented on the Install the tale CLI page (Name who may create organizations, under Managed deployments), on the first-administrator page and in the environment reference, in English, German and French; the manual layer gains AUTH-B9.

The forced-password wall releases on the write's own answer (#3412)

A member whose administrator set their password meets the forced-change wall on first sign-in. Setting the new password left them on it: the page navigates to the dashboard the moment the change resolves, the dashboard gate reads the shared password-expiry cache, and that cache still said expired: true — so it redirected straight back onto the wall with the form emptied and the password already changed. Only a manual reload got the person through. Reproduced end to end against a real stack.

0.5.31 had closed the cache staleness by invalidating the status after the write, which works when the follow-up read lands — but it made the landing depend on a second round-trip, and with that read failing the flow bounced back onto the wall all the same.

The write now answers the question itself: updateUserPassword recomputes the credential's expiry status and POST /api/app/users/update-password returns it as passwordExpiry, and the app publishes what the server just decided into the shared cache instead of asking again — no extra round-trip on the critical path, nothing left to fail between the write and the landing. Against a backend that answers no status — a mid-roll deployment still serving the previous image — the app falls back to re-reading, so the roll itself is safe. Proved in real Chromium with the real query cache, account bootstrap, expiry gate and router (the published status with no re-read; still landing when every re-read fails; the mid-roll fallback), in the adapter test, against real Postgres in the backend integration check, and in the browser end to end including with the status read forced to 503 throughout. Manual box AUTH-F20 covers the administrator-set-password path.

Behaviour changes

  • POST /api/auth/organization/create is judged in the backend against TALE_ORGANIZATION_CREATORS when the variable is set: a listed caller passes, any other signed-in caller is refused with 403 ORGANIZATION_CREATION_FORBIDDEN once the deployment holds an organization, and the first organization always passes. Unset, the route behaves as before.
  • GET /api/app/organizations/capabilities answers canCreate per caller by the same rule instead of true for everyone.
  • A managed deployment that declares organizations.creators no longer refuses organization creation at its proxy and no longer answers the capability probe there; one that declares nothing is unchanged.
  • The CLI refuses a deployment specification whose creator list is empty, longer than 64, repeats an address in another spelling, or names something that is not an address; an environment entry that tries to set TALE_ORGANIZATION_CREATORS is refused as a managed key.
  • POST /api/app/users/update-password answers { ok, passwordExpiry } — the credential's recomputed expiry status — where it answered { ok }; the app leaves the forced-change wall on that value and re-reads only when a backend answers none.

API contract changes

  • None. The OpenAPI document stays at 1.15.0 with 80 paths, 127 operations and 58 schemas, and the Error.code enum keeps its 150 values. ORGANIZATION_CREATION_FORBIDDEN (403) is a code of the Better Auth door, like SIGN_UP_CLOSED; /api/v1 mounts no organization-creation route, and the REST registry records the code as app-only. The passwordExpiry field on the password write is an addition to an /api/app door — the app's own session API, not the machine contract.

Security

  • Organization creation is now a named-list decision made in the backend (#3413). Before, a managed deployment's only protection was the proxy, which the sandbox network bypasses, and a self-managed deployment had no control at all. The default for a deployment that sets nothing is unchanged in both cases — open where it was open, refused at the edge where it was refused — so the release itself widens nothing; an operator who sets the variable narrows creation to the people named.
  • What the list does not cover. A managed deployment that declares no creators keeps today's edge-only refusal, so a signed-in session on the sandbox network can still reach the backend's open create door there, as before this release. Declaring a list closes that door in the backend too. A session is required in every case; the route never answers an unauthenticated caller with anything but 401.
  • No dependency changes in this range, and no advisory is fixed. The @better-auth/oauth-provider advisory noted in 0.5.33 (CVE-2026-67332 / GHSA-p2fr-6hmx-4528, medium) remains open with its workaround in place; the 1.7.0 upgrade is still a separate dependency pull request.

Known issues

  • A managed deployment gets the new behaviour only once its specification declares organizations.creators and a new bundle is applied. Until then its proxy refuses organization creation for everyone, exactly as on 0.5.33 — the release alone changes nothing on such a deployment.
  • The browser round for AUTH-B9 is manual: the picker entry disappearing for an unlisted member, the forbidden page, and the entry returning for a listed one. The backend gate, its wiring, the capability route and the CLI declaration are automated, and the signed-in refusal is proved against real Postgres in the backend integration check. AUTH-F20 (an administrator-set password's first sign-in leaving the wall) is likewise a manual box beside its browser and integration specs.
  • The list is matched against sign-in addresses. A person who signs in through enterprise SSO is matched by the address the directory reports; a renamed address must be renamed on the list.
  • Unchanged from v0.5.33, where each is described in full: the sign-up gate's first-boot race; the boot catch-up that marks provisioned accounts verified asks nobody; the break-glass administrator's password-rotation, single-sign-on-link and memory-adapter limits; the cross-scope webhook guard governs deliveries from that release on; a site's robots policy upgrades at its next scan; a scan waiting on render capacity takes longer by design; the governance pickers list only providers with an active credential; one dependency advisory is open.
  • Unchanged from v0.5.32, where each is described in full: the embedding pacing is proved against a controlled server, its bound is per Tale process, and minTokensPerSecond is a statement nothing verifies; the Kubernetes page's verified scope is one kind cluster, config-data needs RWX or a single node, and Tale ships no Helm chart.
  • Unchanged from v0.5.31, where each is described in full: a managed deployment picks up that release's proxy policy only when a newly prepared bundle is applied; the transcription setting is only as good as the organization's credentials; the six agent-turn fixes are bounded by the pinned Claude Code build they were read from; the 0.5.29 proxy change has been exercised live in TLS_MODE=letsencrypt only; the web tier's backend-URL default lives in the image, not in the generated compose; the scheduled-pack fix does not reach an automation an organization already has; a budget hold covers a turn's first round only; a run still carries no usage or cost; nothing backfills a task timeline.
  • Unchanged from v0.5.20, where each is described in full: the es/co-cc Colombian cédula detector still ships switched off and a locale-agnostic PII toggle still widens national-ID matching to every locale; thinking-block replay on the native Anthropic connector is not done and the live Max-plus-tool-call check is still owed; rag_search embedding calls inside a harness turn are unmetered; the product edit dialog cannot clear a field; the app's skill editor still carries the retired private visibility.
  • Cloud sync, left for later: there is still no Sync now action — the cadence is the fifteen-minute scan, so a reconnected account waits for the next run. A config whose owner leaves the organization is still deactivated silently by a different door, and a source-deleted item is still a status stamp with no bell.
  • Documents indexed before 0.5.27 keep one vector per repeated passage until they are re-indexed; the content hash is unchanged, so only an explicit retry-indexing (or a content change) re-embeds them.
  • The rail's navigation memory has had part of its manual round: the R5 round drove six EN/DE/FR desktop and phone cases covering parts of NAV-F16–NAV-F19; the remaining section, the second-account cases and NAV-B6–NAV-B9 are still unrun.
  • A reply-language directive is a directive: a model may still answer in the prompt's language and nothing on the wire marks a slip.
  • No image input on the REST chat send. A vision model reads an image over REST only on a thread the app continued with an image attachment; the design of an attachments field on the send is recorded as contract debt.
  • No REST door authors or deploys an automation — POST /automations answers 405 by design. Build and deploy in the app, or over the MCP endpoint's save_automation and deploy_automation; the REST key lists, reads, runs and wires triggers.
  • The x-tale-pagination extension is a declaration on the OpenAPI document; generated clients that do not read vendor extensions still branch on the two cursor names until cursor is retired.
  • The app's zip upload of a skill bundle rewrites the bundle and moves updatedAt even when the zip is byte-identical, where PUT /skills/{slug} writes nothing.
  • A tool call the reply cap cut keeps input: {} on the stored tool-call part; the raw text the model emitted is still not on the transcript.
  • Folder names written before 0.5.24 keep their bytes; a sync engine's hub-path lookup can create an NFC twin beside a legacy NFD folder. No backfill ships.
  • Two bounded document readers still filter after their cut; both report an honest truncated, so a caller can tell the answer was cut.
  • Behind a Docker-published port, every IPv6 client arrives as the bridge gateway's address and shares one per-address rate-limit bucket and one audit address until the daemon runs with ip6tables and the reverse proxy's network is IPv6-enabled — an operator item, documented on the Own Compose page.
  • Recorded as contract debt, each with its design in the ledger: a queued send is invisible on the message list until a worker opens it; a webhook delivery the deployed inputs schema refuses moves no trigger stamp; the MCP run_deployed tool keys its idempotency apart from start_run and REST; a page is fetched three to four times per scan; a cancelled run answers trace: null and effects: null where a failed run answers both; approvals and asks have no REST twins; a task cannot be archived or deleted over REST; a webhook bind does not say whether the deployed inputs schema admits a delivery; an exhausted repeatUntil is only a trace note; Website carries no scanStartedAt and the crawler has no page cap, path filter or stop verb of the caller's; website search has no dense leg and its substring fallback stamps score: 0; no Idempotency-Key on the task start; no queue position on a queued send; a corrupt Office document still fails as indexer_error and is retried five times where a PDF lands malformed; no /.well-known/security.txt; no changelog feed on tale.dev; no SDK, collection or per-code table beyond the Error.code enum; GET /notifications rows carry type as a free string and nothing pushes them to a machine caller; a skill keeps no version history on the machine door; the per-task circuit breaker is not built; the messages a conversation snapshot applied are readable only in the app.

Migration notes

  • No migration. The application database stays at 0107 and the knowledge database is unchanged; the boot-time account catch-up from 0.5.33 keeps running and matches nothing once every provisioned account is verified.
  • One new environment variable: TALE_ORGANIZATION_CREATORS, documented in .env.example and in the environment reference in English, German and French. Unset means unchanged behaviour, so no deployment has to act on this release.
  • One optional addition to the managed deployment specification, organizations.creators. A specification that declares nothing behaves as before; a managed deployment whose runtime bundle was prepared by an older CLI is unaffected until a bundle prepared with this release's CLI declares the list.
  • One new error code, ORGANIZATION_CREATION_FORBIDDEN (403), on the Better Auth door only.
  • No image in the stop-gated tier changes. The proxy and db images carry no source change. The managed proxy policy the CLI renders changes only for a deployment that declares creators. A plain tale deploy is the whole upgrade: no --stop, no downtime window.
  • The platform image (the gate, the capability route, the shared allowlist parser, the forced-change wall's answer) and the docs image (three pages in each of English, German and French: the CLI install page, the first administrator and the environment reference) carry source changes. web and ui-docs rebuild with no change of their own. The db, proxy, sandbox, sandbox-runtime, sandbox-buildkitd, sandbox-egress and sandbox-llm-gateway images carry no source change.
  • The CLI has source changes in this range — the organizations.creators declaration, its runtime variable, the policy rendering and the bundle reader — so a managed deployment must move its pinned CLI reference as well as its platform reference to declare the list. The release executables report 0.5.34.
  • @tale/ui and @tale/marketing-ui are pinned by this release as the ui-v0.5.34 and marketing-ui-v0.5.34 tags on their snapshot branches; a consumer outside the monorepo installs "@tale/ui": "github:tale-project/tale#ui-v0.5.34". Neither package has a source change in this range, so both tags are content-identical to their 0.5.33 predecessors.

Upgrading

  • On the 0.5 line (0.5.0 – 0.5.33):

    tale update
    tale deploy

    Nothing in this release needs --stop. A deployment crossing from a version older than 0.5.29 should read that release's notes, which do: its proxy image change is only applied by a --stop deploy. A deployment crossing 0.5.33 runs that release's migration 0107 at boot.

  • Want to limit who may open a new organization? Set TALE_ORGANIZATION_CREATORS to their sign-in addresses in the deployment environment and redeploy; the first organization stays allowed, everyone else loses the Create organization entry and is refused at the API. Leave it unset to keep the open behaviour.

  • Managed deployments move by pinning the CLI and the runtime to this release's commit, preparing a new bundle and applying it with the pinned CLI — see Managed deployments on the CLI install page. To open organization creation to named people, add organizations.creators to the specification before preparing that bundle; without it the proxy keeps refusing creation for everyone. The bundle's backend-local phases run under the interpreted CLI (cli/tale.mjs) that the setup-cli action and bun run --filter @tale/cli build produce beside the executable; the executable from the release page has no interpreted bundle beside it and cannot prepare a managed bundle. On a Linux x64 host whose CPU lacks AVX2, pass linux-baseline: 'true' to the setup-cli action so the bundle embeds the baseline executable.

  • New install:

    curl -fsSL https://raw.githubusercontent.com/tale-project/tale/main/scripts/install-cli.sh | bash
    mkdir tale-05 && cd tale-05
    tale init
    tale deploy

    On a CPU without AVX2 the downloaded executable aborts with Illegal instruction; build it from source with bun run build:linux-baseline in tools/cli.

What's Changed

  • feat(platform): let the operator name who may create an organization by @larryro in #3413
  • fix(platform): release the forced-password wall on the write's answer by @yannickmonney in #3412

Full Changelog: v0.5.33...v0.5.34