Skip to content

cloud: v0.35.0

Choose a tag to compare

@github-actions github-actions released this 09 Oct 14:04
e332566

0.35.0 (2026-10-09)

Features

  • access: let the permission editor count managers as the service does (#775) (f82f707)
  • ai: notice when an offer fits and tell the Assistant, so weaker models make it too (#780) (ec3d0e3)
  • ai: show the Studio check calmly in the chat (#766) (f3c6342)
  • ai: update nessi to 0.17 for clean failed turns and provider-stopped answers (#741) (2ba86f9)
  • assistant: catch misaligned layouts and check the whole page before a Studio app is shown (#758) (1598f02)
  • attach files from Cloud apps in Spaces, Grids, Notebooks and Assistant (#749) (435d7ae)
  • chat: add the chat app behind an unreleased Compose profile (#750) (5e71315)
  • cloud: bound Liquid render time and output size separately (#759) (ce861a7)
  • cloud: keep bearer tokens in URLs out of logs and gateway telemetry (#747) (817b341)
  • cloud: remove location data from photos embedded in notes and comments (#757) (deb0f00)
  • cloud: show a calm error state instead of a frozen app view (#740) (1fd7220)
  • layout: show app badges in the rail and app grid (#769) (9f9587b)
  • notifications: add do not disturb and quiet hours for every app (#779) (ddce3b0)
  • notifications: allow message previews in push payloads (#762) (8f086b7)
  • notifications: group browser notifications and show an app badge (#752) (9c4d77e)
  • oauth: approve a device or app on a calm, centered page (#755) (baff695)
  • save attachments into Files from Mail and other apps (#765) (a6d9ae9)
  • spaces: add a timeline calendar view (#763) (fae29fa)
  • spaces: add task and event templates (#764) (8c98ecd)
  • spaces: color calendar items by their tags and choose what the color shows (#754) (c396691)
  • spaces: say which tasks are overdue in task lists (#774) (9dcfb7e)
  • spaces: show overdue and undated tasks under the timeline (#773) (d7168ea)
  • ui: add a resource card that keeps its height in every state (#746) (01c1a95)
  • ui: add a SignatureInput that draws with finger, pen or mouse or types the name (#782) (b85ebd8), closes #708
  • ui: add a Timeline that shows time as a continuous filmstrip (#751) (fe0980d)
  • ui: add an emoji picker with search, recents and skin tones (#778) (1e291a9)
  • ui: grow the chat composer from one line with formatting on demand (#770) (d5e7ff2)
  • ui: show counts, dots and mention marks in sidebar items (#756) (16122af)

Bug Fixes

  • ai: keep Assistant turns working when an older CLI offers retired client tools (#760) (f9598e7)
  • ai: keep scheduled runs inside their grants instead of failing on a tool they may not use (#776) (5b1f50e)
  • ai: offer audio transcription only when it can run and say why when it cannot (#748) (f63fc3f)
  • ai: stop learning from archived chats (#743) (28e8ba1)
  • assistant: keep CLI replies readable and chat titles as given (#761) (c20e88c)
  • gateway: keep proxied requests on the app's own origin (#777) (01ab1b4)
  • mail: stop stale statistics from slowing incoming automation backfills (#742) (9c8e839)
  • spaces: load video tiles only in the browser so WebKit pages cannot freeze (#781) (53d6b41)
  • ui: keep PdfPreview loading until the PDF has loaded and explain a missing viewer (#767) (938dbb1)
  • ui: make a renewed VideoPlayer address continue where it stopped in WebKit (#768) (be7b95c)

Operator notes

TL;DR: This release contains a security fix in the gateway. Update promptly, starting with the gateway. Before 0.35.0, a request whose path started with // could make the gateway forward it to a foreign host, with the client's cookies and Authorization header (#777). Whether you were exposed depends on your reverse proxy and its version. See Security below for what to look for in access logs and how to revoke sessions. The release also keeps bearer tokens in URLs out of logs and gateway telemetry (#747) and limits Liquid render time and output size (#759). For operators, the rest of the update is additive. There are no new environment variables, the JetStream reservation does not change, and the only Compose change is the new app-chat service. It sits behind the unreleased profile, so a normal docker compose up does not start it (#750). Core creates notifications.quiet_times on start and Spaces creates three template tables. Both changes are additive and safe to roll back. Customized built-in Skills show Update available (assistant-code-mode 63, cloud-spaces 4, cloud-weather 2). Review your AI model profiles: Anthropic now defaults to 8,192 output tokens, and Ollama profiles with contextWindow now allocate that window (#741). Run cld plugins update --all for the Assistant and Spaces plugins. App authors should check the island error and @k2b/stdlib 0.28 mutation changes under "npm and public API" (#740). #783 is in the tag but missing from the changelog above (see "Also in this release").

Reference pages at this tag: Security fix: the gateway keeps requests on the application's host, Emergency signing-key revocation, AI provider behavior with Nessi 0.17, A failing island shows a notice, Browser notification updates, Do not disturb and quiet hours, Image upload privacy operations, Logging, Render a Liquid template, Save files into providers, Scheduled AI tasks need attention, Unreleased applications.

Security

Update promptly. Deploy the gateway first. The related hardening below ships in the images of the other applications in this release.

  • The gateway forwarded // paths to foreign hosts. This is fixed (#777). The gateway treated a request path starting with //, for example //evil.example/x, as a scheme-relative reference. It then sent the request, with the client's cookies and Authorization header, to the host named in the path, and returned that host's response, including its headers, under your Cloud address. Content from that host could therefore run in the user's browser with the rights of a Cloud page. This affected HTTP and WebSocket. The gateway now always forwards to the host and port of the matched application's baseUrl and collapses a leading run of slashes into one. Inner // and percent escapes reach the application unchanged. compose.prod.yml runs behind your external Traefik and does not pin its version:
    • Not exposed this way: Traefik v2.11.24 or a later v2.11 release, or v3.3.6 or later, with the default sanitizePath: true. Traefik then collapses duplicate slashes before the gateway sees the request.
    • Exposed: an older Traefik, including v3.0 to v3.3.5, an entrypoint with sanitizePath: false, another reverse proxy that passes the path unchanged, or a gateway port that clients can reach directly.
    • If you were exposed: update, then check the reverse proxy's access logs for request paths that start with //. If such requests came from untrusted sources, treat the sessions of the users who sent them as exposed and revoke them, and review what those accounts did at that time. To invalidate every session at once, follow Emergency signing-key revocation. API credentials and OAuth grants have their own lifecycle. If such requests could have carried an Authorization header, revoke those credentials too.
    • Related hardening, with no configuration change:
      • Sign-in redirects, the configured home path, Command paths and returnTo (including the Mail composer's return address) reject targets whose dot segments resolve to //host.
      • Proxy Auth returns to the protected host's root when X-Forwarded-Uri would leave that host or cannot be parsed. It ignores an X-Forwarded-Host or X-Forwarded-Proto value that is not a plain host or scheme.
      • Run now under Gateway operations > Jobs and the Grids last-visited base no longer redirect outside Cloud.
  • Bearer tokens in URLs no longer reach logs or gateway telemetry (#747, closes #685). Paths below /share/<app>/, /api/mail/public-attachments/ and /app/mail/a/ are recorded as :token. Elsewhere, UUIDs become :id and credential-shaped segments become :token. This covers Venue and Spaces calendar feeds, the Grids public form API, Tools webhooks and Pulse dashboards. The gateway's Upstream unavailable and WebSocket connect-failure logs contain the redacted template, the error type and a transport error code such as ConnectionRefused, but never the error message or the query. Request logs in every application now record the matched route template. Entries written before the update keep the raw paths until your log and telemetry retention removes them.
  • Liquid templates are limited while they render (#759, closes #686). Before this release, a template with nested loops could block an application process for up to five minutes. A render now stops once it exceeds 1,000 ms or 300,000 UTF-8 bytes of output, and application-specific output caps still apply. The time is checked on each template, loop iteration, filter call and operator. A slow custom filter cannot be interrupted, and its time is checked when it returns. In Grids, all HTML cells of one read or export share 2 seconds. In Mail, the signature segments of one composed body share 1 second. Heavy templates can now fail with render_timeout, render_too_large or render_memory_limit, both in previews and when sending. Grids names these errors in English and German. Simplify the template or reduce its data. These limits are application API options, not deployment settings.

Deploy order

  1. Before the update:
    • Reverse proxy: check your Traefik version and sanitizePath (see Security).

    • AI model profiles (#741):

      • Anthropic profiles without an explicit limit now default to 8,192 output tokens, thinking included, and budget reservations use that default.
      • Gemini output usage now includes thinking tokens, so reported costs and budget use can exceed the visible answer's token count.
      • An Ollama profile with contextWindow now sends it as num_ctx, so Ollama allocates that window instead of its memory-dependent default. Check that the server has enough memory for it, or clear the field.
      • OpenRouter now receives the task reasoning level in its reasoning object instead of as a top-level reasoning_effort.
    • Customized built-in Skills keep their old text. Linked copies show Update available under Admin > AI > Skills:

      • assistant-code-mode moves to template 63 (#758).
      • cloud-spaces moves to 4 (#764, #774).
      • cloud-weather moves to 2 (#748).

      Reset customized copies after the update, otherwise agents miss the stricter Studio checks, Spaces templates, the overdue flag and the fallback to forecasts by coordinates. A reset replaces all content and keeps no history, so export a Skill first if you need its customizations. Unmodified linked copies update on the next start. Older copies without a template link stay unchanged until an administrator selects Link to template.

    • There are no new environment variables, no configuration changes and no new JetStream streams.

  2. Gateway first (#777, #747). No schema change.
  3. Core (#752, #762, #779). Its startup creates notifications.quiet_times. Core serves the updated browser notification service worker. Browsers pick it up on the next page load, or during a push or notification click once 24 hours have passed since the last check. Older workers ignore the new group, badge and preview fields. Core runs the AI runtime, so the provider, learning, scheduled-run and offer changes take effect with Core.
  4. Spaces (#764). Its startup creates spaces.item_templates, spaces.item_template_tags and spaces.item_template_assignees with their indexes. The migration is idempotent, and the rows cascade with Spaces, tags and users.
  5. All other applications in any order. Each one picks up the redirect hardening, request-log redaction, the island error notice and photo metadata removal in its own services.
  6. Do not start app-chat (#750). This release publishes 27 images instead of 26, because the new cloud-app-chat image is built and published. Its compose.prod.yml service has profiles: ["unreleased"], so a plain docker compose up, the release smoke and the upgrade preflight leave it out. It is for test installations only and has no row in the deployment requirements yet. If your own tooling deploys every image listed in release.json, exclude it.
  7. cld profiles: run cld plugins update --all.
    • The assistant plugin (#761) prints readable replies in print mode and turns watch, keeps --title past the first message, and runs code_open on any machine with Playwright Chromium installed or CLOUD_CLI_CHROMIUM set to a local Chromium.
    • The spaces plugin adds templates ls|show and add --template [--date | --no-date], and ls --json now returns overdue (#764, #774).
    • Older Assistant plugins work again, because the server now ignores retired client tools such as code_interact instead of rejecting the turn (#760).
    • cld itself has no functional change. The cloud-cli skill in the release assets updates the device sign-in text (#755).

Rollback

  • Gateway (#777): do not roll back below 0.35.0, because an earlier gateway forwards // paths to foreign hosts again. If you must, make sure the reverse proxy collapses duplicate slashes (Traefik v2.11.24 or later v2.11, or v3.3.6 or later, with sanitizePath: true) and that clients cannot reach the gateway port directly.
  • Core (#779): an earlier Core ignores notifications.quiet_times. Quiet time then stops working, and the saved settings are kept.
  • Spaces (#764): an earlier Spaces ignores the template tables. Templates are unavailable until you update again, and nothing is lost.
  • Photos (#757): images uploaded in the meantime stay stripped.
  • Any application: an earlier image also drops the token redaction in its logs (#747), the Liquid render limits (#759), the redirect hardening and photo metadata removal for new uploads.
  • Nothing else in this release changes the schema or the configuration. If you roll back past 0.34, the 0.34 rollback steps also apply.

Assistant and AI

  • Archived chats no longer feed learning (#743). Archiving a chat stops any pending learning from it, including workflow-pattern evidence. Nothing is saved from a learning run whose chat was archived or deleted while it ran. A request that was already waiting for model capacity can still reach the model, but its result is discarded. Restoring a chat lets learning consider only turns that finish after the restore.
  • Scheduled runs stay within their grants (#776). Background runs only find, load and call app capabilities that their mandate policy and grants allow. Other tools report not_allowed. If a grant or a fixed input rejects a call, the model gets a tool error and can continue, so the task no longer switches to needs_attention because of it. A missing, changed, revoked or expired mandate, a target app that rejects the sponsor, an unknown action outcome, or a tool that needs approval or a browser still fails the run.
  • The Assistant notices when an offer fits (#780). This applies to a second format correction in a row, a tone correction of a mail draft, and a reference to earlier work such as "like last week". In those turns, Cloud adds one short instruction to offer a Skill or to remember the preference once, at the end of the reply. The instruction repeats every limit: organization and Project instructions, memories and a user's request for no suggestions still take precedence. A question such as "What can you do for me?" now adds a bounded Recent work section built only from Assistant data: the number of the user's other chats, up to five titles and up to eight Cloud items used in them. It does not reveal which apps hold data for the user. Scheduled runs do not get these hints, and there is no setting.
  • Audio transcription is offered only when it can run (#748). transcribe_audio, the composer microphone and dictation appear only if an enabled audio model has credentials, an allowed data boundary and an Assistant grant for the user. Otherwise, the model is told the concrete reason and asks the user to contact an administrator. Users without a grant get transcription_access_denied.
  • Studio checks are stricter (#758, #766).
    • code_check now fails an app that shows a JavaScript engine error in its own alert, status or live region, or in <output>.
    • It warns about misaligned flex or grid rows, and about visible NaN, undefined, Invalid Date or [object Object], in the app and in PDFs from cloud.pdf.render.
    • Screenshots now cover the whole page, up to 2,000 px tall, and the agent is asked to review every screenshot.
    • Apps that passed before can now need a correction round.
    • In the chat, the check shows as "Checking the app" with a worded outcome, and built-in tool names appear in English or German.
  • Failed and provider-stopped turns (#741). Refusals and content filters now end as failed turns that offer Continue, and their tool calls never run. The provider's reported usage for failed and aborted turns is kept and booked once.
  • Conversation titles (#761): the first message replaces only a default title, so a title set when the chat was created stays.

Notifications

  • Do not disturb and quiet hours for every app (#779). Under My account > Notifications, people can pause browser notifications for up to 366 days, or set up to seven weekly quiet periods in a time zone they choose. During quiet time, Core drops recommended browser deliveries as suppressed with do_not_disturb or quiet_hours and ends their waiting fallbacks. Nothing is postponed, and no email is sent in place of the push. Required deliveries and email chosen first still go out. These codes appear in notification history and under Observability > Notifications; they are not delivery failures. Apps need no code for this.
  • Grouping, badges and previews (#752, #762). Apps can group browser notifications, set the installed app's unread badge, and send a short preview text below the title. No built-in app uses these fields yet, so notifications look as before. A preview appears on lock screens and paired devices. It is fixed when the app sends the notification, and Cloud does not check access again for queued, retried or fallback deliveries. Once the push service accepts a push, Cloud cannot recall it, and an offline device can still receive it up to 24 hours later. Before you enable a preview for sensitive content, check the app's own setting to turn previews off.
  • App badges in the rail and app grid (#769). No built-in app declares one yet. Once an app declares a badge, every visible tab sends one GET to its badge route after the page loads, then at most one per minute, plus one more whenever a page asks for a fresh count after a change. Hidden tabs send nothing. Count these requests when you size the app.

Files, photos and attachments

  • Location data is removed from embedded photos (#757).
    • New JPEG, PNG and WebP uploads lose GPS position, camera make and model, serial numbers, XMP, IPTC, comments, maker notes and embedded thumbnails without re-encoding. This applies to Notebooks attachments, Spaces task attachments, Grids record files and form title images, Assistant chat, Project and app files, Venue images, profile avatars, the installation logo and model profile logos.
    • Orientation, color profiles, transparency and animation stay.
    • Malformed images are rejected with 422 MALFORMED_IMAGE. Saving a stored value that contains a malformed inline image answers 422 until that image is replaced.
    • Files keeps originals, Mail draft attachments stay byte-identical, and HEIC, AVIF, TIFF, SVG, GIF, BMP and video are stored unchanged.
    • Existing images, retained revisions, backups and exports are not rewritten. There is no switch and no backfill. Inline images inside a larger value, such as model profile logos, a Form configuration or Venue settings, are stripped when that value is saved again.
  • Attach from Cloud apps (#749). Attach now opens the shared chooser (This device first, then every Cloud app with files) in Spaces, Grids file fields, Notebooks, and Assistant chats and Projects, as Mail already did. Each app's upload limits and checks still apply. On public pages where the capability catalog refuses access, Attach opens the device's file dialog.
  • Save to Files (#765). A Save action sits next to Download in Mail attachments and the details panel, Spaces task media, Notebooks attachments, Grids file fields and record versions, and Assistant chat and Project files. Save only creates new files: if a name is taken, it asks for another one and never overwrites.
  • Previews and video (#767, #768, #781). PDF previews stay in the loading state until the PDF has loaded. Browsers without an inline PDF viewer, such as Chrome on Android, show a hint and no longer download the file automatically. A renewed video address resumes where it stopped in Safari and on iOS. Spaces item pages with a video attachment no longer freeze in WebKit.

Spaces

  • Task and event templates (#764). Space admins manage templates in a new Templates tab. The + button offers them, and they work the same through the API, capabilities and cld. Assignees who lost access to the Space drop out of a template. New endpoints: /templates and /templates/:id/draft. Item create accepts an optional checklist and assignCreator, and task.create and event.create accept templateId, date and timeZone.
  • Timeline view (#763, #773). The calendar gets a fifth view, cv=timeline. A row below it shows up to five overdue open tasks of the Space and up to five open tasks without a deadline that are assigned to the reader. The workspace view endpoint takes an optional from/to range of at most 366 days and includeTray (default true).
  • Calendar colors (#754). Items take the color of their first tag, then of their status, then gray. A "Color by" row in the Scope menu switches to status, priority or person, and the URL keeps the choice. Tasks with a due date show a colored checkbox marker, and urgent and high priority add a red flag.
  • Overdue flag (#774). task.list, task.focus and POST /api/spaces/:id/items/filter return overdue. It is true only for an open task whose deadline lies before the start of today, in the application time zone for capabilities and the viewer's time zone for REST. The existing --due overdue / deadlineFilter: "overdue" filter still checks only the deadline, so with completed or all statuses it can return tasks with overdue: false.

Other applications

  • OAuth (#755). Code entry, consent, device approval and every result page are now standalone centered pages. The tab title names the result. Server behavior, CSRF checks, tokens and audit are unchanged.
  • Island error notice (#740, #783). A render error replaces only the failing island, dialog or floating window with a localized notice, instead of freezing the app view. The error still reaches reportError().
  • Grids (#775): the permission editor locks the same last-manager row that the service would refuse to change, so saves no longer fail with LAST_MANAGER when a None grant shadows an Admin grant.
  • Mail (#742): stale Postgres statistics no longer slow incoming automation backfills.
  • Weather (#748): without weather.geo_url, city search now returns WEATHER_CITY_SEARCH_NOT_CONFIGURED (still a 500) instead of a generic error, and capability errors point to forecasts by coordinates.

npm and public API

@k2b/cloud 0.29.0 (additive; depends on @k2b/ui 0.19.0, @k2b/ssr 0.15.0, @k2b/stdlib 0.28.0 and @k2b/nessi 0.17.0)

  • Island errors (#740): defineApp puts each island in its own error boundary with Cloud's notice. Remove root ErrorBoundary wrappers that an island only had for protection. With @k2b/stdlib 0.28, render errors no longer set a query's or mutation's error(). onSuccess runs after data() and loading() update, and an error it throws rejects mutate() and retry(). Move that handling to try/catch. A Link from @k2b/ssr/nav without onNavigate is now a plain link.
  • New exports:
    • redactSensitivePath() from @k2b/cloud/services (#747). The request logger now records the matched route template. Carry link tokens in a route parameter with at least 16 random bytes.
    • stripImageMetadata() and stripImageDataUrlMetadata() from @k2b/cloud/services/image-metadata (#757). Call them in the service that owns a content image upload.
    • saveFiles(), SaveFilesButton, saveFilesLabel() and SAVE_FILES_ICON from @k2b/cloud/browser/files (#765).
    • nav.badge in defineApp and refreshAppBadges() from @k2b/cloud/browser/app-badges (#769). A badge route must lie below the app's own routes.
  • Notifications:
    • render() takes group, badge and preview (#752, #762). An invalid group or badge fails in send(), and grouping requires an app ID matching ^[a-z][a-z0-9-]*$.
    • A preview is normalized to at most 200 code units, reaches only the browser channel, and is dropped if the push would exceed 4,096 bytes. Apps that send previews should give operators a setting to turn them off.
    • browserNotificationClient adds closeGroup(), setBadge() and quietState(). The server adds notifications.user.quiet.get(userId) (#779).
  • Templates (#759): Liquid rendering takes renderTimeoutMs (default 1,000) and renderMaxBytes (default 300,000). Failures are LiquidTemplateError with reason render_timeout, render_too_large or render_memory_limit. Template previews pass the reason on as code.
  • Access (#775): PermissionEditor takes an optional effectiveEntries.
  • AI:
    • clientToolIds ignores unknown names. At most 64 distinct names of 1 to 64 letters, digits, _ or - are allowed (#760).
    • There is a new turn error, provider_stopped (#741).
    • Built-in tool configuration errors now say what is missing (#748).
  • Paths (#777): returnTo and Command paths reject paths whose dot segments resolve to //host. The gateway collapses a leading run of slashes before it forwards a request.

@k2b/ui 0.19.0 (additive)

  • New components:
    • ResourceCard (#746) and Timeline (#751).
    • AppWorkspace.SidebarItemStatus (#756) and ChoiceChips (#764).
    • EmojiPicker and EmojiPicker.Popover (#778). Its dataset is about 80 kB compressed and loads on first use. The emojibase-data license ships in dist/licenses.
    • SignatureInput with signatureToPng() and parseSignature() (#782). Treat a submitted signature as untrusted: read it on the server with parseSignature(), store only its rebuilt result, and treat null as missing or invalid.
    • Chat.Composer with variant="conversation" (#770).
  • New options:
    • Calendar takes customViews (#763), display: "marker" and accessibleDetail (#754).
    • Menu sections take layout: "row" (#754), and Dropdown link rows take an action (#751).
    • FileDropzone and ChatComposer.fileSelection take choose (#749).
    • Lightbox takes image actions and handles keys only while focus is inside it (#765).
  • Behavior changes:
    • Dialogs and floating windows get their own error boundary. A dialog whose content throws while it opens shows the notice and resolves undefined (#740).
    • PdfPreview stays loading until the PDF has loaded, and an autoLoad preview requests nothing in a browser without an inline PDF viewer (#767).
    • A renewed VideoPlayer address resumes through a #t= fragment (#768).
    • Charts no longer read closed props during cleanup (#783).
    • The @k2b/ssr peer range now includes 0.15.

Also in this release

These commits are in the tag but missing from the changelog above:

  • #783 fix(ui): never read a closed view's props during cleanup (ed81fff). It merged while the release PR was queued. Before, a chart, the Help documents island or a Grids background action inside a non-keyed <Show> threw Stale read from <Show>. when that view closed, and the island stopped responding or showed the error notice. They now unmount cleanly. The fix also ships in @k2b/ui 0.19.0 and @k2b/cloud 0.29.0.
  • #745 docs (JetStream storage), hidden by release-please as a docs commit. Since 0.34.0, Core reserves about 4.1 GiB of JetStream storage per replica, and all built-in applications together about 16 GiB. This release adds nothing to that. If max_file_store is too small, NATS refuses the streams with insufficient storage resources available and Core does not start.
  • #739 docs (outgoing mail), hidden as a docs commit. The Outgoing mail pages now show how to move an application off its own SMTP account: the app switches to mail from @k2b/cloud/services, and the operator turns the old account into a sender profile assigned to that app.
  • The test-only commits #730 and #753 are hidden by design.