Tale v0.5.35
0.5.35 carries 8 merged pull requests. The trusted-headers door becomes an organization's own feature: an administrator switches it on, caps the role a proxy may assert and mints keys under Settings > Enterprise SSO, the door resolves the organization from the key, and a proxied visitor is signed in on the app's first request without ever seeing a sign-in page; the same page gains an Embedding card naming the web origins that may show Tale inside a frame. REST gains the doors an external system needs to act for a person — answer a run's pending question, decide a task's review — naming the verified member the gesture is recorded for (API contract 1.16.0). Team and member lists refresh in every open session after a team write or a directory push, a Knowledge import lands in the folder you have open and a file can be moved afterwards, an auto-retried agent step continues its conversation instead of starting over, two settings surfaces stop reporting configuration as missing when it is set, and every data table's two unnamed header cells get their names. One migration (0108) and one column Better Auth adds at boot; no image in the stop-gated tier changes, so the upgrade is tale update followed by a plain tale deploy. Two environment variables of the retired deployment-wide trusted-headers mode are removed.
Highlights
Trusted headers belong to the organization (#3418)
GET /api/trusted-headers/authenticate — the door an authenticating reverse proxy hands its users through — used to be a deployment-wide mode: one environment switch, one environment secret, every proxy sign-in landing in the earliest organization that had an owner or administrator, the sign-in page redirecting every visitor, and a rotation meaning a redeploy. None of that serves a deployment that hosts several organizations, where a hosting application must sign its users into its organization and no other.
The credential is now organization data, in the shape the SCIM token already has. Under Settings > Enterprise SSO, the Trusted headers card holds the switch, the Highest role a proxy may assert and the keys: a key is a random bearer value answered exactly once, only its SHA-256 hash is stored, an organization holds at most 10 live keys, and revocation is a stamp that keeps the row as the audit trail behind every session the key minted. Migration 0108 creates the two tables.
The door reads the key from the key header (Remote-Internal-Secret by default; an Authorization header is ignored, so a REST API key can never be mistaken for it), resolves the organization by the hash — never from a header, a body or a path — refuses a paused organization or a revoked key, charges unknown keys to the source address, and caps Remote-Role at the organization's ceiling; Owner is never assertable through a proxy. The single-sign-on organization-binding contract holds: a member of that organization signs in, an address the deployment has never seen becomes a new member with the asserted role, and an existing account from another organization is refused before any write. A member's seat follows the asserted role on every sign-in, with the same update_member_role audit a manual change writes; an Owner seat never moves, and GET /api/app/members/me reports the role the organization gate enforces so the app never shows a stale seat. Remote-Teams accepts plain names — Finance,Operations, the form most authenticating proxies emit — as well as id:name pairs; teams are matched and created by name, where a bare list used to be read as "no teams" and revoked synchronized memberships. Every sign-in is audited as trusted_headers_sign_in naming the key; every write to the card is audited too. The admin surface is /api/app/trusted-headers.
The deployment-wide switch (TRUSTED_HEADERS_ENABLED, TRUSTED_HEADERS_INTERNAL_SECRET) and the sign-in page's global redirect are removed without a compatibility shim.
A proxied visitor is signed in without a sign-in page (#3418)
A GET the app makes for itself — the session probe, a read under /api/app/* — that carries the organization's key and the identity header but no session cookie is answered signed in: the backend mints the session on the spot, the cookie rides on the response, and the request goes on as if the browser had sent it. Never on a POST, never for a cross-site fetch, never while a session cookie is present, never while the app's hold cookie stands. The dashboard opens directly.
The door itself answers a success with a 302 to the in-app return path and the cookie — no page of its own. A refusal of a hand-off the app started goes back to the sign-in page with its reason, rendered there in the app's own words in English, German and French with a recovery hint; a proxy-routed or terminal refusal is answered as a plain page with its status code. The sign-in page still hands off by itself when it lands on a proxied request (a stale cookie, say), shows a refusal instead of retrying, and keeps a one-minute marker so a hand-off that came back without a session shows the form with Try again rather than a redirect loop. A public probe, GET /api/app/sso/discovery/trusted-headers, tells the page whether the request carries the proxy's headers at all — presence only, uncacheable, no second key oracle.
Because the proxy owns the session, the account menu offers no Log out for a proxied session. After an inactivity sign-out the app sets a short-lived hold cookie (tale_handoff_hold, fifteen minutes): the backend does not mint while it stands, and the sign-in page waits behind Continue with automatic sign-in so the inactivity notice is seen before the session comes back.
An organization names who may embed Tale (#3418)
Framing was refused outright — frame-ancestors 'none' and X-Frame-Options: DENY on every response — so an application that wanted Tale inside its own page had no knob. The new embedding governance policy ({ enabled, frameAncestors[] }) names the web origins allowed as frame ancestors: https://host[:port], lowercase, or plain http:// on loopback only, at most 16, matched by a character-restricted pattern because the value lands in a response header. It rides the existing policy file lane — <org>/governance/embedding.yml, the generic /api/app/governance/policies/embedding routes, tale config apply — and the seed catalog ships the closed default.
The web tier scans every organization's policy (cached, like the storage-origin scan) and the shell's Content Security Policy carries frame-ancestors 'self' plus the union; X-Frame-Options, which cannot express an allowlist, is left off while anything is admitted and returns to DENY when nothing is. The canvas preview admits the same list; the trusted-headers door stamps its framing headers per organization once the key has named one; every other backend response keeps the fixed DENY. The Embedding card on the Enterprise SSO page holds the switch and the origin list in English, German and French with a Swiss German override.
Act for a member over REST: answer a run's question, decide a task's review (#3419)
A run parked on waitingFor: "ask" and a task parked in in_review both wait on a person. When that person works in another application that mirrors the desk, the machine caller can now relay their gesture and name them as the actor, so Tale's timeline and audit trail record the person and not the key.
GET …/runs/{runId}/askanswers the live question asPendingAsk— the sentence, an optional structuredquestionsset (QuestionSet: up to four questions of two to four labelled options each, answerable in the person's own words too), the node that asked and theexpiresAtdeadline — orask: null.POST …/runs/{runId}/asks/{askId}records the answer, resumes the run in the same transaction and mirrors the answer onto the task timeline as the answerer's own comment. Both exist in the organization scope (/api/v1/runs/…) and the project scope (/api/v1/projects/{id}/runs/…). Withoutactorthe key answers as itself (answeredBy: "api-key:<userId>"). A closed question answers 409HUMAN_ASK_NOT_PENDING, an expired one 409HUMAN_ASK_EXPIRED, one this run did not ask 404HUMAN_ASK_NOT_FOUND, a blank answer 400EMPTY_ANSWER.GET /api/v1/projects/{id}/tasks/{taskId}/reviewanswers the task's status and its pendingTaskReview, orreview: null;POSTon the same path decides it, and hereactoris required because a review is always a person's decision.approveis the board's move to Done — the member's own project access and the organization'sreview_policyapply exactly as there (403REVIEW_INDEPENDENT_REVIEWER_REQUIREDorREVIEW_COMPETENCE_REQUIRED), and a task with open subtasks answers 409TASK_HAS_OPEN_SUBTASKS.request_changesneedscommentandworkflowSlug: it withdraws the review, puts the comment on the timeline and starts the workflow again on the task, which reads the comment as feedback; the answer carries therunIdto poll. A task not in review answers 409TASK_NOT_IN_REVIEW. Every decision is audited astask.review_relayed, every relayed answer asautomation.ask_answered, naming the member and the key.actor.emailis resolved against the organization with the notification export's rule — exactly one active, verified membership: 404ACTOR_NOT_FOUND, 409ACTOR_AMBIGUOUS, 403ACTOR_UNVERIFIED, 403ACTOR_DISABLED. Every answer returns the resolvedactorUserId; pinned asactor.userId, an address that has since moved to another account answers 409ACTOR_REBOUNDinstead of acting as its new holder. A member who may not see the project, or may not write the task, answers 403ACTOR_FORBIDDEN.- Naming an actor is a right of its own: an Owner or Admin key has it by role, any other key holder needs the new
tale:rest.act-ascapability, granted and revoked in the competence register like the export capability.GET /api/v1/meanswers it ascapabilities.actAs; anactorsent without it answers 403ROLE_FORBIDDENbefore any member is looked up. Both writes charge the execute bucket on top of the general REST bucket.
The API reference documents the two doors in a new Act for a member section in English, German and French.
Team and member lists stay live in every session (#3414, #3415)
Team create, rename and delete ride Better Auth's own organization endpoints, so no app write adapter ever saw them: a second tab, and every teammate with the Teams page or the team picker open, kept the stale list until a reload, and the bulk delete refreshed nothing at all. The plugin's lifecycle hooks now emit one team invalidation hint per write, after the plugin's own commit and non-fatal by construction, so every connected session refreshes through /events; the bulk delete refetches once per batch. The SCIM provisioning lane had the same gap on the other door into the same tables — a directory sync that added a member, renamed a group, moved somebody between teams or de-provisioned an account left every open Members and Teams page showing the pre-sync list. Every SCIM write now emits its hint inside the transaction that performs it (member for the user lane, team for the group lane, one team hint per team a de-provisioning shrank), gated on an actual change so a directory's scheduled full re-push, which moves nothing, hints nothing. Both ends of each hint share one named constant (TEAM_HINT_ENTITY, MEMBER_HINT_ENTITY) — the drift the shared vocabulary exists to prevent.
Imports land in the open folder, and a file can be moved (#3420)
Import placement mirrored the provider's own path and nothing else, so files picked at the top of the OneDrive or Google Drive picker went to the Knowledge root however deep in the tree you were standing, and nothing could correct that afterwards. Both import routes take destinationFolderId through one shared gate — hub scope only, ordinary folder access, and the destination's team wins over the picker's selection; a path-less pick lands in the destination, a provider subfolder is mirrored underneath it, and the depth cap counts from the destination. The row menu gains Move to folder… for a file, listing every hub folder the caller can see by full path plus the root. The document update behind it gains two rules the create path has enforced since the hub shipped: a destination outside the caller's teams is refused with FOLDER_NOT_ACCESSIBLE, and a team folder stamps its team on what lands inside it. Files only — moving a folder has no door here — and documents already at the root stay there.
An auto-retried agent step continues its conversation (#3421)
An automation agent node's in-node auto-retry re-kicked a fresh harness conversation: same prompt, same sandbox workspace, but the model started reasoning from zero and re-read every file the dead turn had already read. An errored settle now keeps the harness's conversation handle, the stepper hands it to the re-kick, and the retry resumes that conversation with a short continuation prompt naming the cut as an infrastructure failure, not the agent's doing. A turn that died before announcing a handle, whose sandbox session is gone or that never launched still starts fresh over the preserved workspace; the retry budget and the fifteen-minute progress refresh are unchanged. The execution-logs page says so in English, German and French.
Two statuses stop blaming configuration that is set (#3417)
The OAuth apps card called Google Drive Not configured on a deployment whose Drive import worked: Drive has two lanes consenting against one vendor app, and the card read only the connector lane's variables, not the import lane's. The row now consults both and is configured when either answers. The backend's plaintext-secrets warning said SOPS_AGE_KEY was not set on a deployment where it was; the branch never consulted the key, and the text hardcoded the message. It now branches on the key's presence and, with a key set, names the real next step: re-save the secrets so the write path encrypts the file. Neither change alters resolution — only the two reports were wrong.
Two data-table header cells get their names (#3416)
A <th> with no discernible text fails WCAG 2.1 AA (axe empty-table-header). DataTable named its row-action column already; the expander column's header was permanently empty, and the select column's header was empty for the whole loading state, because the skeleton masks the select-all checkbox that carries the name. Both labels now live in the header renderer, the select label emitted only while skeletonizing so the live checkbox keeps owning the name. The expander's cell button, which announced a hardcoded English "Expand row" / "Collapse row" to every locale, reads the same aria keys in English, German and French. @tale/ui changes in this range for it.
Behaviour changes
GET /api/trusted-headers/authenticateresolves the organization from the presented key and refuses a request without one, with an unknown or revoked key, or against an organization whose card is off; it answers a success with a 302 to the in-app return path instead of a page of its own. The deployment-wide mode is gone:TRUSTED_HEADERS_ENABLEDandTRUSTED_HEADERS_INTERNAL_SECRETare no longer read, and the sign-in page no longer redirects every visitor.- A cookieless GET the app makes for itself that carries an organization's key and the identity header is answered signed in, the session minted on the spot; a proxied session offers no Log out, and an inactivity sign-out pauses the automatic sign-in for fifteen minutes.
- On every proxied sign-in the member's seat is moved to the asserted role, capped at the organization's ceiling and audited as
update_member_role; an Owner seat never moves.Remote-Teamsaccepts plain team names; a bare list no longer revokes synchronized memberships. - Tale's pages answer
frame-ancestors 'self'plus the union of every organization's enabledembeddingorigins and dropX-Frame-Optionswhile anything is admitted; with nothing admitted,frame-ancestors 'none'andDENYas before. POST …/runs/{runId}/asks/{askId}andPOST …/tasks/{taskId}/reviewtake anactor; the second requires it. A key holder who is neither Owner nor Admin needstale:rest.act-asto name one. Both writes charge the execute bucket.- Team create, rename and delete, and every SCIM write that changes something, emit invalidation hints; open Teams and Members pages in every session refresh without a reload.
- A OneDrive or Google Drive import lands in the folder open in the app; a file gains Move to folder…; a document update that names a destination folder outside the caller's teams is refused with
FOLDER_NOT_ACCESSIBLE, and a team folder's team is applied to a document moved into it. - An auto-retried agent step resumes the failed turn's conversation when that turn had announced its handle.
- The OAuth apps card reports Google Drive configured when either the connector or the import lane holds a deployment app; the plaintext-secrets warning names the right next step when
SOPS_AGE_KEYis set. - Every data table names its expander column header and, while loading, its select column header; the expander button is announced in the user's language.
API contract changes
- The OpenAPI document moves from 1.15.0 to 1.16.0: 85 paths (from 80), 133 operations (from 127), 62 schemas (from 58). New paths:
/api/v1/runs/{runId}/ask,/api/v1/runs/{runId}/asks/{askId},/api/v1/projects/{id}/runs/{runId}/ask,/api/v1/projects/{id}/runs/{runId}/asks/{askId},/api/v1/projects/{id}/tasks/{taskId}/review(GET and POST). New schemas:Actor,PendingAsk,QuestionSet,TaskReview. No existing path changes. Me.capabilitiesgainsactAs(required, boolean).- The
Error.codeenum grows from 150 to 164 values:ACTOR_AMBIGUOUS,ACTOR_DISABLED,ACTOR_FORBIDDEN,ACTOR_NOT_FOUND,ACTOR_REBOUND,ACTOR_UNVERIFIED,EMPTY_ANSWER,HUMAN_ASK_EXPIRED,HUMAN_ASK_NOT_FOUND,HUMAN_ASK_NOT_PENDING,REVIEW_COMPETENCE_REQUIRED,REVIEW_INDEPENDENT_REVIEWER_REQUIRED,TASK_HAS_OPEN_SUBTASKS,TASK_NOT_IN_REVIEW. Seven of them —EMPTY_ANSWER, the threeHUMAN_ASK_*, the twoREVIEW_*andTASK_HAS_OPEN_SUBTASKS— were app-only codes before and are on the machine contract now; theACTOR_*codes andTASK_NOT_IN_REVIEWare new. X-Tale-Api-Versionanswers1.16.0. No existing operation changes shape;Megains a field and theError.codeenum grows, so a client generated from 1.15.0 keeps working.- Two new codes stay app-only, on
/api/app/trusted-headers:TRUSTED_HEADER_KEY_LIMIT(409, an eleventh live key) andTRUSTED_HEADER_KEY_NOT_FOUND(404). The door's refusals are pages, not envelopes.
Security
- The trusted-headers credential moves from one deployment-wide shared secret to per-organization keys (#3418). A key is shown once and stored only as a SHA-256 hash; the organization it signs into is decided by the key, never by anything the request names; the asserted role is capped per organization and can never be Owner; a paused card refuses every key without revoking one; unknown keys are charged to the source address and refused with 429 past the budget; the
Authorizationheader is ignored on the door so a REST key is never a hand-off credential. The proxy contract stands and is documented: strip client-suppliedRemote-*headers, add the key only on the hand-off request, keep the backend reachable only through the proxy. Anyone holding a key can sign in as any member the proxy names within that organization — it is a password, held by the proxy, rotated from the card. - The transparent sign-in mints a session from request headers without a page. It fires only on a GET the app makes for itself, only with the key and the identity header present, only without a session cookie, never for a cross-site fetch and never under the hold cookie; a deployment whose organizations hold no keys never mints. The hand-off's return path is validated to same-origin paths, and the sign-in page's redirect goes to the origin the browser is on, never a configured site URL.
- Framing is opened only by an organization's explicit allowlist (#3418). Each origin is validated by a strict pattern before it is interpolated into the
Content-Security-Policyheader; a file that fails the schema is dropped with a warning, never emitted. The shell is one document for the deployment, so an origin any organization admits may load the shell — the session inside the frame still decides what it can reach, and the door judges per organization. The browser sends the session cookie into a frame only from a same-site page (SameSite=Laxis unchanged); a cross-site frame shows the sign-in page. - A key with
actAsrecords gestures for other people (#3419). An Owner or Admin key has the right by role; any other key needs thetale:rest.act-ascompetence an administrator grants, scoped to the organization, optionally expiring, revoked with the membership. The actor must be an active, verified member, their own project and task access decides what the gesture may do,actor.userIdpins the person against a re-bound address, and every relayed answer and decision is audited naming both the member and the key. - The document move door fails closed (#3420). The document update accepted a destination folder without asking whether the caller can see it and without applying the destination's team; the app's move action makes it a door, so it now refuses an inaccessible destination and stamps the team, the two rules uploads already follow.
- No dependency changes in this range, and no advisory is fixed. The
@better-auth/oauth-provideradvisory 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 frame carries the signed-in session only from a same-site host page — a subdomain of the host, or Tale served under the host's own domain. A cross-site frame shows the sign-in page, which, with the loop guard, offers Try again instead of bouncing. The embedding allowlist is per organization but the shell's policy is the union across organizations. The dev server's preview keeps a fixed
SAMEORIGIN. - Revoking a trusted-header key does not end the sessions it started, and turning the card off refuses every key without ending sessions either.
- The browser rounds for the trusted-headers card, the hand-off, the embedding card, the page-less sign-in and the door refusals (
AUTH-F21–AUTH-F24,AUTH-B10) are manual, as is the two-session team round trip (SET-F41). The door, the mint, the framing headers, the seat move and the refusals are proved against real Postgres in the backend integration check; the hints are covered per write by their unit suites. - Approvals still have no REST twin. A run parked on
waitingFor: "approval"is decided in the app only; the ask half of that debt is paid in this release, the approval half is recorded with its design in the ledger. - Moving a document into a team folder changes who can see it, because the folder's team replaces the document's — the same rule an upload follows. Moving a folder has no door; documents already at the root are not moved by this release.
- The auto-retry resumes only a turn that announced its conversation handle. A turn that died before announcing one, whose sandbox session is gone, or that never launched starts fresh over the preserved workspace, as before.
- The Google Drive row counts a deployment app from either lane. An organization-level app still does not make the row configured, by design.
- Unchanged from v0.5.34, where each is described in full: a managed deployment gets the organization-creator behaviour only once its specification declares
organizations.creatorsand a new bundle is applied; theAUTH-B9andAUTH-F20rounds are manual; the creator list is matched against sign-in addresses. - 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
minTokensPerSecondis a statement nothing verifies; the Kubernetes page's verified scope is one kind cluster,config-dataneeds 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=letsencryptonly; 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-ccColombian 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_searchembedding calls inside a harness turn are unmetered; the product edit dialog cannot clear a field; the app's skill editor still carries the retiredprivatevisibility. - 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 andNAV-B6–NAV-B9are 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
visionmodel reads an image over REST only on a thread the app continued with an image attachment; the design of anattachmentsfield on the send is recorded as contract debt. - No REST door authors or deploys an automation —
POST /automationsanswers 405 by design. Build and deploy in the app, or over the MCP endpoint'ssave_automationanddeploy_automation; the REST key lists, reads, runs, answers asks and wires triggers. - The
x-tale-paginationextension is a declaration on the OpenAPI document; generated clients that do not read vendor extensions still branch on the two cursor names untilcursoris retired. - The app's zip upload of a skill bundle rewrites the bundle and moves
updatedAteven when the zip is byte-identical, wherePUT /skills/{slug}writes nothing. - A tool call the reply cap cut keeps
input: {}on the storedtool-callpart; 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
ip6tablesand 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
inputsschema refuses moves no trigger stamp; the MCPrun_deployedtool keys its idempotency apart fromstart_runand REST; a page is fetched three to four times per scan; a cancelled run answerstrace: nullandeffects: nullwhere a failed run answers both; approvals have no REST twin; a task cannot be archived or deleted over REST; a webhook bind does not say whether the deployedinputsschema admits a delivery; an exhaustedrepeatUntilis only a trace note;Websitecarries noscanStartedAtand 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 stampsscore: 0; noIdempotency-Keyon the task start; no queue position on a queued send; a corrupt Office document still fails asindexer_errorand is retried five times where a PDF landsmalformed; no/.well-known/security.txt; no changelog feed on tale.dev; no SDK, collection or per-code table beyond theError.codeenum;GET /notificationsrows carrytypeas 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
- One migration, 0108 (
0108_trusted_header_keys.sql): two new tables,app.trusted_header_settingsandapp.trusted_header_keys, with a unique index on the key hash and a listing index per organization. Nothing existing changes, so the previous image keeps serving while the new one migrates. The application database moves from 0107 to 0108; the knowledge database is unchanged. - One column Better Auth adds at boot: the session record gains
trustedOrganizationIdbeside the existingtrustedRole, through Better Auth's own schema step that runs after the numbered migrations (logged asapplying better-auth migrations). Nullable, ignored by the previous image. - Two environment variables are removed:
TRUSTED_HEADERS_ENABLEDandTRUSTED_HEADERS_INTERNAL_SECRET. A deployment that set them ran the deployment-wide trusted-headers mode; on 0.5.35 they are ignored, and its proxy sign-ins are refused until an administrator turns the card on in the organization the proxy should sign into and gives the proxy a key from it. Delete the two variables from the environment.TRUSTED_SECRET_HEADERand the otherTRUSTED_*_HEADERnames keep their meaning; the environment reference is updated in English, German and French. No variable is added;.env.exampleis unchanged. - One new organization configuration file,
governance/embedding.yml, seeded closed (enabled: false, no origins) for a new organization. An existing organization without the file behaves exactly as before: nothing may frame it. A CLI older than this release does not know theembeddingpolicy type and refuses the file intale config apply. - Fourteen error codes join the machine contract and two app-only codes appear; see API contract changes. New audit actions:
trusted_headers_enabled,trusted_headers_disabled,trusted_headers_policy_updated,trusted_header_key_created,trusted_header_key_revoked,trusted_headers_sign_in,automation.ask_answered,task.review_relayed. - No image in the stop-gated tier changes. The
proxyanddbimages carry no source change, and the managed proxy policy the CLI renders is unchanged. A plaintale deployis the whole upgrade: no--stop, no downtime window. - The platform image (the door, the mint, the two cards, the REST doors, the hints, the import destination, the retry resume, the two statuses) and the docs image (five pages in each of English, German and French: the API reference, Enterprise SSO, execution logs, authentication configuration and the environment reference) carry source changes. The web, docs and ui-docs images also carry the
@tale/uichange through the design system they build on. Thedb,proxy,sandbox,sandbox-runtime,sandbox-buildkitd,sandbox-egressandsandbox-llm-gatewayimages carry no source change. - The CLI has no change of its own in this range, but the reference tree it embeds — the platform's shared modules, its core, and the seed catalog with the new embedding policy — does, so the release executables are rebuilt and differ from 0.5.34; they report 0.5.35. A managed deployment should move its pinned CLI reference together with its platform reference, as always.
@tale/uiand@tale/marketing-uiare pinned by this release as theui-v0.5.35andmarketing-ui-v0.5.35tags on their snapshot branches; a consumer outside the monorepo installs"@tale/ui": "github:tale-project/tale#ui-v0.5.35".@tale/uichanges in this range (the data-table header names and their three message files);@tale/marketing-uidoes not, so its tag is content-identical to its 0.5.34 predecessor.
Upgrading
-
On the 0.5 line (0.5.0 – 0.5.34):
tale update tale deploy
Migration 0108 and Better Auth's column are applied at boot. 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: itsproxyimage change is only applied by a--stopdeploy. A deployment crossing 0.5.33 runs that release's migration 0107 at boot as well. -
Ran the deployment-wide trusted-headers mode? Remove
TRUSTED_HEADERS_ENABLEDandTRUSTED_HEADERS_INTERNAL_SECRETfrom the environment. In the organization the proxy should sign users into, an Admin opens Settings > Enterprise SSO, turns on Accept sign-ins from a trusted proxy, chooses the role ceiling, creates a key and gives it to the proxy in place of the old secret, in the sameRemote-Internal-Secretheader. A proxied member is signed in on the app's first request; routing the proxy's/log-into the hand-off address remains supported. To show Tale inside the application's own page, list that page's origin under Embedding on the same page. -
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. The bundle's backend-local phases run under the interpreted CLI (
cli/tale.mjs) that thesetup-cliaction andbun run --filter @tale/cli buildproduce 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, passlinux-baseline: 'true'to thesetup-cliaction so the bundle embeds the baseline executable. A hosted deployment that wants to admit an application's proxy adds that proxy's public origin toadditionalOriginsand creates the organization the proxy will sign users into before the administrator enables the card. -
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 withbun run build:linux-baselineintools/cli.
What's Changed
- fix(platform): keep every session's team list live after a team write by @yannickmonney in #3414
- fix(ui): name the data table's two unlabelled header cells by @yannickmonney in #3416
- fix(platform): emit invalidation hints from the SCIM provisioning lane by @yannickmonney in #3415
- feat(platform): act for a verified member on ask answers and reviews by @larryro in #3419
- fix(platform): resume the failed agent conversation on auto-retry by @larryro in #3421
- fix(platform): stop two statuses blaming config that is already set by @Israeltheminer in #3417
- fix(platform): land imports in the open folder and allow moving a file by @Israeltheminer in #3420
- feat(platform): own trusted-header keys per organization by @larryro in #3418
Full Changelog: v0.5.34...v0.5.35