Skip to content

Tale v0.5.29

Choose a tag to compare

@larryro larryro released this 16 Sep 03:44
3cde850

0.5.29 is a patch tag on the 0.5 line that carries far more than its fixes — read it as a minor. Twenty-three changes since 0.5.28, and three of them undo product that was quietly broken: budget caps were documented but no server path enforced them, no scheduled automation had started on any install since 0.5.17, and IMAP mail ingest threw on every message. It ships one forward-only migration on the application database, an API contract that moves from 1.12.0 to 1.13.0, one new environment variable, a changed shipped automation catalog — and a changed proxy image, so the upgrade is tale update followed by tale deploy --stop.

Highlights

Budget caps now refuse work, and members can see their own (#3372, #3373, #3375)

Policies & Limits let an admin cap tokens, cost and requests per member, team, role and API key, and the documentation said those caps applied to chat. They did not. The only thing stopping a chat turn was the composer's send block in the browser: the REST send, regenerate, edit, both arena columns and a parked send all went through, and a cap could be overshot N-fold by N sends racing it, because a turn books its usage only once it settles.

Every chat lane now measures the sender on the server before the turn spends anything. A reached cap refuses the send — 429 in the app with the cap named, 429 BUDGET_EXCEEDED over REST, budget_exceeded on the reply for a deferred or REST turn that was accepted before the cap closed. And a live turn no longer spends invisibly: it holds its first round's worst case — the assembled prompt plus the output reserve, at the model's catalog rates — on its own row for as long as it runs, and every admission of the organization queues behind one lock and reads the holds the previous one wrote. That is what migration 0106 adds.

The caps also measure what they claimed to. Team shared caps count the usage of the team's current members, read through membership, instead of a ledger column that chat, tool and agent rows never carried. API-key caps are enforced and metered for the first time — a keyed turn books its key. Role caps now also bind voice output and video-link ingest.

Beside the enforcement, members get an answer to "how much have I used". Settings › Usage is a personal page every role can open: each personal token, cost and request cap with a used-of-limit meter and its reset time, the team and organization caps measured against everyone's combined usage, and uploaded storage against the upload policy. It answers from the same gate that would refuse the next request, so what it shows is what will happen.

And Request usage credits now reaches the people who can grant it. It used to write an organization-wide system notification, so the whole company's bell announced that a named member had hit their limit while the banner told that member the request went "to your admins". Each owner and admin now gets a personal row and email; a repeated click rewrites the unread row instead of stacking another; and when nobody else can grant credits the button no longer claims it asked.

Scheduled automations start again (#3381)

Every shipped pack that ships a schedule has been unable to start since 0.5.17 — on any install, on every release since. The scheduler starts a run with the trigger wrapper ({trigger: 'schedule', firedAt}), which the run gate validates against the deployed version's inputs schema, and all eight packs declared additionalProperties: false without trigger or firedAt. The refusal landed before a run row existed, so there was nothing in the run list to see. The eight packs now declare the wrapper, a guard test fails any future pack that closes its inputs against it, and the trigger documentation no longer shows firedAt as a string when the scheduler sends epoch milliseconds.

Read the upgrade note: this fixes what a new organization is seeded with. Provisioning skips an automation an organization already has, so an existing install keeps its refused version until someone edits that automation's input schema and deploys a new version.

IMAP mail ingest works again (#3382)

imap-smtp.get_message threw on every message in every mailbox, so a self-hosted instance reading mail over IMAP imported nothing. The root overrides forced linkify-it 6 on the tree; mailparser pins 5.x and calls it as a function, and version 6 is a dual build that resolves under require to a namespace object, not a callable — so mailparser threw while still loading. Sending was unaffected, which is what hid it: the product looked healthy and the scheduled sync reported success on every pass, with only output.inbound.reason naming the failure.

The override was a deliberate ReDoS remediation, not a stray pin, so it is not simply deleted: mailparser moves to 3.9.28, which pins linkify-it 5.0.2 — patched for both advisories — and the override comes off with nothing left to protect. A runtime test now imports mailparser, imapflow and nodemailer through the resolver production uses and parses a message end to end, because importing the entry alone would not have caught this.

An instance needs both this and the pack fix above before a scheduled mailbox sync does anything.

One documentation frame behind both sites (#3370, #3371)

docs.tale.dev and ui.tale.dev each carried a fork of the same chrome, and the forks had drifted: the design-system site hand-rolled its outline, its "Open in" menu and its edit link, put the page title in the trail, and had no footer. The chrome is now one @tale/ui/docs/* family — rail, phone drawer, header strip, article, outline, neighbour cards, edit link, page actions, footer, back-to-top, 404 and the ⌘K static-index search — and a site feeds it only its navigation, search index and copy.

Readers of the design-system guide get the product docs' shape: the h1 in the article, the page actions in the header strip, a footer carrying the theme switcher, the repository link and the llms indexes, and a 404 with suggestions inside the frame. Both sites gain three fixes the shared frame brings: the header strip is 52px, ending on the rail's logo-row line instead of one pixel below it; every frame link matches its route exactly, so the logo and the first page's Previous card no longer claim aria-current="page" on a locale home; and the chrome is hidden in print. Both sites also now declare the same theme behaviour — with nothing saved the page follows the operating system, and the footer switch saves Light, Dark or System.

One deployment on several origins (#3384)

A managed deployment can now answer on more than one HTTPS origin as fully respected origins, including behind an external TLS terminator, rather than redirecting to a primary name.

  • The proxy trusts that terminator. In TLS_MODE=external another proxy terminated TLS and Tale's Caddy serves plain HTTP, so the browser's scheme arrives only as the terminator's X-Forwarded-Proto — which Caddy honours only from a peer it trusts. Trusting nobody, every lane forwarded http. The entrypoint now renders trusted_proxies from the new TRUSTED_PROXIES variable (CIDR ranges, or private_ranges by default) in strict mode, and the explicit X-Forwarded-Proto {scheme} pins are gone. Every other TLS mode terminates in this Caddy and trusts no peer, as before.
  • Browser file links are signed for the origin the browser is on, when the published object-store endpoint is itself one of the deployment's site origins. Background work, which has no browser, keeps the configured endpoint.
  • The managed specification gains additionalOrigins (literal origins or environment references), written to the runtime's ADDITIONAL_SITE_URLS: managed like every other key, compared by adoption and ready-state checks, and refused at preparation time against a runtime whose proxy image is too old to carry the trust placeholder. Native identity, the OIDC issuer, passkey rpID and email links stay on the primary origin.

Catalog records: one view, one edit, one delete (#3369, #3357)

Products, contacts, knowledge entries and websites each had their own way to show, edit and delete a record — four dialog widths from 384 to 1100px, some with no minimum height, and different delete wording. All four now share one 448px entity dialog with a floor of min(34rem, 90dvh), one details layout (image or icon tile, summary and badges, a facts grid ending in a copyable id, then record-specific sections), one row menu reading View · Edit · record actions · Delete, and one Delete {name}? confirmation. Edit morphs inside the same card instead of closing the view to open a second overlay, and Cancel brings the details back.

Focus comes back to the row's menu button whenever a dialog closes, including after Edit → Cancel — rows cannot be focused with the keyboard, so that menu is how a keyboard user opens a record at all. Two long-standing defects fall out: Edit inside contact details never opened (closing the details unmounted the edit dialog nested in them), and knowledge-entry version history never appeared (the adapter returned a bare array where the contract expects {entry, versions}).

Website scan failures stop dumping sandbox JSON and getaddrinfo into the list — status stays a badge, the reason is a muted caption, the dump is on hover — and a scan that indexed nothing reads as a teaching empty instead of a hollow 0 words row.

An opt-in confidentiality notice, configurable at last (#3366)

The line under the chat composer was both invisible and unconfigurable: a freshly scaffolded organization gets enabled: false, nothing in the app could change it, and only organizations without the policy file showed a notice — a missing file read as "on". It is now opt-in everywhere (missing file, disabled policy, loading read and failed read all show none) and editable under Settings › Governance › Policies & limits: an instant switch and the notice text per shipped language, edited in the shared locale tabs with an untranslated pill and a 280-character cap per language. Saving keeps what the editor does not show, including regional texts.

Turning it on no longer pushes the composer up a quarter-second after the page becomes usable: the chat loader warms the policy, the pre-hydration script reserves the row from the remembered text, and the live footer holds that row until the read settles. The notice also moves to the caption tier, which it was already styled as but did not render as.

Also in this release

  • A document is prepared once, not once per slice (#3368). Indexing a large document re-ran the whole-document preparation — secret scan, PII policy, chunker, content hash, repeat map — for every slice, making it O(slices²), and the synchronous PII pass held the worker's event loop, stalling every other job on it. A 2 MB document with PII detection on drops from 9.8 s to 0.56 s; 600 KB drops from 1.06 s to 0.17 s.
  • The queued-send tray settles when its turn starts (#3367). A send parked behind attachment processing kept its Queued row and left its own bubble out of the transcript for the whole generation — the reply streamed under an empty gap. Both now flip the moment the turn opens.
  • The chat list splits between Projects and Chats (#3383). Projects takes the height its folders need up to half the panel, Chats takes the rest, each scrolls under its own header, and a chat dropped on the room under a short list lands in Chats — previously drag-and-drop measured a folder scrolled out of view and filed the chat into a hidden project.
  • Provider changes apply without a page reload (#3363). Enabling the first key left the agent Model picker saying "No models found" and the providers page saying there was no usable credential, while the backend already answered six models: those reads were cached outside the provider_credential hint entity. All of them now key under it, so Refresh catalogs and a model-serving policy save reach them too.
  • Document sources read as icons (#3386). The Source column spelled its source out, so a German "OneDrive (synchronisiert)" wrapped onto two lines into the RAG status; Google Drive showed a bare logo that never said whether it synced; and the preview sidebar printed internal slugs like google_drive for anything outside its own three-provider map. One provenance map now feeds both, as a vendor logo plus a small glyph, with the words as the tooltip and the accessible name.
  • Task field edits appear on the timeline (#3379). Title, description, priority, labels, attachments, start and due date and reviewer each write their own event, with label ids resolved to catalog names. Nothing is backfilled: tasks edited before this stay as sparse as they are.
  • Every unknown route gets the app's not-found page (#3362). A typo outside the dashboard rendered the router's bare "Not Found" string in the top-left corner under the marketing tab title; the in-dashboard 404 was hand-rolled from the error display's style. Both now render the app's own empty-state dead end, and every miss sets the "Page not found" title.
  • New credentials open with a name filled in (#3365) — the vendor's own name, numbered past that vendor's existing credentials ("OpenRouter", then "OpenRouter 2"), taken once when the vendor is picked. An untouched suggestion closes without a discard prompt. A second OAuth grant is now "Slack 2" rather than "Slack (2)".
  • Phone layout: the composer no longer moves when the loading skeleton becomes the real page (#3374), the automation canvas stops clipping its zoom controls below lg (#3364), and the mobile action dock pads its buttons evenly once they wrap (#3364).
  • The image preview dialog keeps its size (#3380) — a floor of min(600px, 70dvh), so a small image no longer produces a cramped box with the zoom controls against its edges.

Behaviour changes

  • A chat send — in the app, on regenerate or edit, in either arena column, parked behind attachments, or over REST — is refused on the server when a budget cap that binds the sender is reached. The app shows a refusal toast naming the cap; REST answers 429 BUDGET_EXCEEDED with Retry-After; a send accepted before the cap closed settles its reply with errorCode: budget_exceeded.
  • A live chat turn holds its first round's worst case against the caps until it settles, so concurrent sends can no longer each pass a cap with room for one.
  • Team shared caps count the current members of the team; API-key caps are enforced on REST and metered; role caps also bind voice output and video-link ingest.
  • Settings › Usage is a new personal page for every role, linked from the chat budget banner's View usage.
  • Request usage credits notifies each owner and admin personally instead of writing an organization-wide row, and answers that it sent nothing when the requester is the only owner or admin.
  • The Inbox status and unread tiles count only conversations the viewer can open; they used to show organization-wide totals above a list that showed a subset.
  • Shipped automation packs that carry a schedule declare the scheduler's trigger and firedAt input, so a scheduled occurrence starts a run. Existing organizations keep the automation they were seeded with.
  • A mailbox read over IMAP imports messages again.
  • Settings › Governance › Policies & limits carries a Confidentiality notice section; the notice under the composer is opt-in and shows nothing for an organization without the policy, with it switched off, or while the read is in flight.
  • The Documents Source column shows a vendor mark plus an import glyph instead of words, and a failing sync is a red mark that opens the same reason and reconnect dialog.
  • A task's timeline records edits to its title, description, priority, labels, attachments, start date, due date and reviewer.
  • Products, websites, contacts and knowledge entries share one details dialog, one row menu, one delete confirmation and one size; a row click morphs into edit in the same card rather than opening a second overlay.
  • The chat list gives Projects up to half the panel and Chats the rest, each scrolling under its own header.
  • Both documentation sites render one shared frame and follow the operating-system theme when nothing is saved; the design-system guide's page title moves into the article, its page actions into the header strip, and it gains a footer.
  • An unknown URL anywhere in the app renders the app's not-found state and sets the "Page not found" document title.
  • Add credential prefills Name from the provider or connector.
  • In TLS_MODE=external, the proxy accepts X-Forwarded-Proto and X-Forwarded-For from the peers named by TRUSTED_PROXIES (private_ranges by default) and reads them strictly, right to left. Other TLS modes ignore the variable and trust no peer.
  • A browser on an additional configured origin gets object-store links signed for that origin, when the published endpoint is itself one of the deployment's origins.

API contract changes

The OpenAPI document moves from 1.12.0 to 1.13.0 (dated 2026-09-15). The surface is unchanged at 80 paths, 127 operations and 58 schemas. The Error.code enum rises from 149 to 150 values with BUDGET_EXCEEDED.

Added

  • BUDGET_EXCEEDED in the Error.code enum, answered 429 by POST /api/v1/threads/{id}/messages and POST /api/v1/projects/{id}/threads/{threadId}/messages when a budget cap that binds the key holder is reached — their own, one of their teams', the organization's or this API key's. Nothing is queued. Retry-After names the wait in whole seconds until the period resets.
  • Six data fields on that refusal: scope (user | team | org | apiKey), period (daily | weekly | monthly), limitCode (TOKEN_LIMIT | COST_LIMIT | REQUEST_LIMIT), used and limit in the cap's unit, and resetsAt in epoch milliseconds.
  • budget_exceeded in the chat errorCode classification: a cap reached after a send was accepted, so the turn never ran. A send made once the cap is already reached is refused up front with the 429 instead.

Changed

  • Both chat send operations document the budget refusal in their description and in their 429 response, beside the existing RATE_LIMITED case. The budget check runs after the idempotency claim, so a replay of an accepted send still answers its 202.

Security

  • The Inbox count tiles published organization-wide totals to every member (#3378). The list door filters conversation by conversation through the one definition of inbox visibility; the two count doors did not filter at all. A member saw how much traffic exists that they cannot open, including the size of the unassigned triage queue, above a list showing them a subset. Both count queries now carry a SQL mirror of the visibility rule and are pinned by tests that go red on each way of widening it — in particular on any clause that would admit a null assignee, which is the admin-only triage case and the way this failure would publish an entire inbox.
  • A credit request named the member to the whole organization (#3373). Hitting a usage limit and clicking Request usage credits wrote a system notification, which every member can see, announcing that a named person had run out of credit — while the banner said the request went to the admins. It now writes a personal row to each owner and admin only.
  • Budget caps were a documented control that nothing enforced (#3375). Only the browser's composer stopped a chat turn: a REST send, an edit, a regenerate or a parked send spent past any cap, an API-key rule was never enforced or metered at all, and concurrent sends could overshoot a cap several times over because usage books only on settle. All of those now refuse on the server, and a live turn holds its worst case while it runs.
  • TRUSTED_PROXIES is a trust boundary (#3384). In TLS_MODE=external the proxy now believes a forwarded scheme and client address from the ranges it names, defaulting to every private and loopback range. The proxy container publishes port 80, so a client that can reach that port from inside a trusted range could claim an HTTPS connection it never made — allow only your TLS terminator to reach it, and narrow TRUSTED_PROXIES to the range it connects from. Every other TLS mode trusts no peer. The proxy refuses to start on a value that is neither a CIDR range nor private_ranges.
  • The ReDoS remediation is kept, not dropped (#3382). The linkify-it override that broke IMAP parsing was covering GHSA-22p9-wv53-3rq4 and GHSA-v245-v573-v5vm in the component that parses attacker-controlled mail. Rather than removing it and reopening those, mailparser moves to 3.9.28, whose own pin is the patched 5.0.2. The tree resolves linkify-it 5.0.2; no advisory exposure is introduced or reopened.

Known issues

  • The proxy change ships unexercised on a running deployment. The hosted fleet has not moved past 0.5.27, so the rendered trusted_proxies block, the strict right-to-left read and the removal of the X-Forwarded-Proto {scheme} pins have been proved by the CLI's compose-parity rendering and by tests, not by a live external-TLS deployment. An operator on TLS_MODE=external should verify sign-in callbacks, secure cookies, uploads and streaming through the full path after this upgrade.
  • The pack fix does not reach an existing install (#3381). Provisioning skips an automation an organization already has, so an upgraded instance keeps the version whose input schema refuses its own scheduler. Edit that automation's inputs to admit trigger and firedAt and deploy a new version; a new organization is seeded correctly.
  • A budget hold covers a turn's first round. A turn that calls tools runs up to five model rounds, each billing its full prompt again, and only the first round's worst case is held while it runs. Concurrent sends can no longer each pass a cap with room for one, but a long multi-round turn can still settle above the cap it was admitted under.
  • Nothing backfills a task timeline (#3379). Edits made before this release wrote audit rows only and do not appear; a label deleted from the catalog renders as its raw id rather than dropping the row.
  • 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.
  • 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.
  • 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. A site that has not been scanned since 0.5.27 has no stored robots rules until its next scan.
  • 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 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.
  • 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 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; robots.txt $ end-anchors and Allow: lines are not honoured (prefix and * rules are), and 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; a run carries no usage or cost; 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 silently; 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.
  • One of 0.5.28's known issues is closed here: the documentation chrome is no longer duplicated between the two sites.

Migration notes

  • One migration, forward-only and rolling-safe. 0106 budget_reservations on the application database, applied by the backend at boot inside the advisory lock while the previous image keeps serving. It adds four columns to app.generations — user_id and api_key_id (nullable text) and reserved_cost_cents / reserved_tokens (NOT NULL DEFAULT 0) — with ADD COLUMN IF NOT EXISTS, and creates app.budget_admissions (org_id primary key, admitted_at_ms), the row an organization's budget admissions lock and bump. The previous image holds nothing and never reads the new table, so a mid-roll window is a window where the older replica's turns are admitted the way 0.5.28 admitted them. There is no down migration by design. The task_labels_project_id_name_key constraint 0.5.22 kept for its rolling deploy is still in place; dropping it is a follow-up migration.
  • tale deploy --stop is required. The proxy image carries source changes — the trusted_proxies rendering in the entrypoint and the removal of the X-Forwarded-Proto {scheme} pins in the Caddyfile — and the proxy is in the stop-gated tier, which a default deploy leaves running with a warning. Without --stop the new image is built and pinned but never takes over, and an additionalOrigins deployment will not work. Expect a short availability blip while the proxy container is recreated.
  • One new environment variable, and two that existed but were undocumented:
    • TRUSTED_PROXIES — TLS_MODE=external only. Whitespace-separated CIDR ranges, or private_ranges (the default). The proxy refuses to start on any other value; the other TLS modes ignore it. Documented on TLS and domains and in the environment reference.
    • WORKER_CONCURRENCY (default 5) — jobs one backend-worker process runs at once: indexing, crawls, automations and agent turns share it.
    • KNOWLEDGE_DB_POOL_MAX (default 10) — connections one backend process opens to the knowledge corpus. An indexing job holds one while it commits a slice, so keep it at or above WORKER_CONCURRENCY; the worker now warns at boot when it is below. Count both per replica against the corpus database's max_connections, like DATABASE_POOL_MAX.
  • The shipped automation catalog changes. The eight packs that ship a schedule (GitHub review/triage, Gmail sync/triage, IMAP sync/triage, Outlook sync/triage) declare the scheduler's trigger and firedAt inputs. Organizations created after this upgrade get the fixed packs; existing ones keep the automation they were seeded with, as described under Known issues.
  • No other configuration file changes shape. The confidentiality-notice policy keeps its fields — requireAcknowledgment and version are still stored and preserved on save, and still drive nothing.
  • The platform, proxy, docs and ui-docs images carry source changes; the web image has none of its own and rebuilds on the shared @tale/ui package. The db, sandbox, sandbox-runtime, sandbox-buildkitd, sandbox-egress and sandbox-llm-gateway images are unchanged. The CLI has source changes in this range (additionalOrigins and its validation, adoption, receipt and preparation checks), so a managed deployment must move its pinned CLI as well as its runtime; the release executables report 0.5.29.
  • @tale/ui and @tale/marketing-ui are pinned by this release as the ui-v0.5.29 and marketing-ui-v0.5.29 tags on their snapshot branches; a consumer outside the monorepo installs "@tale/ui": "github:tale-project/tale#ui-v0.5.29". @tale/ui gains the docs/* frame family, the static-index search engine moved out of the docs site, locale-tabs, bottom-tab-bar's placeholder, the entity dialog family and Dialog's size="entity".

Upgrading

  • On the 0.5 line (0.5.0 – 0.5.28):

    tale update
    tale deploy --stop

    The migration runs at boot. --stop is needed for the proxy image; see Migration notes. A deployment crossing from a version older than 0.5.27 should read that release's notes too.

  • 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 answer on more than one origin, declare additionalOrigins in the deployment specification; the CLI writes ADDITIONAL_SITE_URLS and keeps native identity, the OIDC issuer, passkey rpID and email links on the primary origin. 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 instead.

What's Changed

Full Changelog: v0.5.28...v0.5.29