Repository navigation
cloud: v0.35.0
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 andAuthorizationheader, 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'sbaseUrland collapses a leading run of slashes into one. Inner//and percent escapes reach the application unchanged.compose.prod.ymlruns 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 anAuthorizationheader, 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-Uriwould leave that host or cannot be parsed. It ignores anX-Forwarded-HostorX-Forwarded-Protovalue 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.
- Sign-in redirects, the configured home path, Command paths and
- Not exposed this way: Traefik v2.11.24 or a later v2.11 release, or v3.3.6 or later, with the default
- 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:idand credential-shaped segments become:token. This covers Venue and Spaces calendar feeds, the Grids public form API, Tools webhooks and Pulse dashboards. The gateway'sUpstream unavailableand WebSocket connect-failure logs contain the redacted template, the error type and a transport error code such asConnectionRefused, 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_largeorrender_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
- 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
contextWindownow sends it asnum_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
reasoningobject instead of as a top-levelreasoning_effort.
-
Customized built-in Skills keep their old text. Linked copies show Update available under Admin > AI > Skills:
assistant-code-modemoves to template 63 (#758).cloud-spacesmoves to 4 (#764, #774).cloud-weathermoves to 2 (#748).
Reset customized copies after the update, otherwise agents miss the stricter Studio checks, Spaces templates, the
overdueflag 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.
-
- Gateway first (#777, #747). No schema change.
- 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 newgroup,badgeandpreviewfields. Core runs the AI runtime, so the provider, learning, scheduled-run and offer changes take effect with Core. - Spaces (#764). Its startup creates
spaces.item_templates,spaces.item_template_tagsandspaces.item_template_assigneeswith their indexes. The migration is idempotent, and the rows cascade with Spaces, tags and users. - 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.
- Do not start
app-chat(#750). This release publishes 27 images instead of 26, because the newcloud-app-chatimage is built and published. Itscompose.prod.ymlservice hasprofiles: ["unreleased"], so a plaindocker 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 inrelease.json, exclude it. cldprofiles: runcld plugins update --all.- The
assistantplugin (#761) prints readable replies in print mode andturns watch, keeps--titlepast the first message, and runscode_openon any machine with Playwright Chromium installed orCLOUD_CLI_CHROMIUMset to a local Chromium. - The
spacesplugin addstemplates ls|showandadd --template [--date | --no-date], andls --jsonnow returnsoverdue(#764, #774). - Older Assistant plugins work again, because the server now ignores retired client tools such as
code_interactinstead of rejecting the turn (#760). clditself has no functional change. Thecloud-cliskill in the release assets updates the device sign-in text (#755).
- The
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, withsanitizePath: 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 toneeds_attentionbecause 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 gettranscription_access_denied. - Studio checks are stricter (#758, #766).
code_checknow 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 Dateor[object Object], in the app and in PDFs fromcloud.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
suppressedwithdo_not_disturborquiet_hoursand 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
previewtext 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
GETto 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:/templatesand/templates/:id/draft. Item create accepts an optional checklist andassignCreator, andtask.createandevent.createaccepttemplateId,dateandtimeZone. - 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 optionalfrom/torange of at most 366 days andincludeTray(defaulttrue). - 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.focusandPOST /api/spaces/:id/items/filterreturnoverdue. 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 withoverdue: 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_MANAGERwhen 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 returnsWEATHER_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):
defineAppputs each island in its own error boundary with Cloud's notice. Remove rootErrorBoundarywrappers that an island only had for protection. With@k2b/stdlib0.28, render errors no longer set a query's or mutation'serror().onSuccessruns afterdata()andloading()update, and an error it throws rejectsmutate()andretry(). Move that handling totry/catch. ALinkfrom@k2b/ssr/navwithoutonNavigateis 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()andstripImageDataUrlMetadata()from@k2b/cloud/services/image-metadata(#757). Call them in the service that owns a content image upload.saveFiles(),SaveFilesButton,saveFilesLabel()andSAVE_FILES_ICONfrom@k2b/cloud/browser/files(#765).nav.badgeindefineAppandrefreshAppBadges()from@k2b/cloud/browser/app-badges(#769). A badge route must lie below the app's own routes.
- Notifications:
render()takesgroup,badgeandpreview(#752, #762). An invalidgrouporbadgefails insend(), 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.
browserNotificationClientaddscloseGroup(),setBadge()andquietState(). The server addsnotifications.user.quiet.get(userId)(#779).
- Templates (#759): Liquid rendering takes
renderTimeoutMs(default 1,000) andrenderMaxBytes(default 300,000). Failures areLiquidTemplateErrorwithreasonrender_timeout,render_too_largeorrender_memory_limit. Template previews pass the reason on ascode. - Access (#775):
PermissionEditortakes an optionaleffectiveEntries. - AI:
- Paths (#777):
returnToand 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) andTimeline(#751).AppWorkspace.SidebarItemStatus(#756) andChoiceChips(#764).EmojiPickerandEmojiPicker.Popover(#778). Its dataset is about 80 kB compressed and loads on first use. Theemojibase-datalicense ships indist/licenses.SignatureInputwithsignatureToPng()andparseSignature()(#782). Treat a submitted signature as untrusted: read it on the server withparseSignature(), store only its rebuilt result, and treatnullas missing or invalid.Chat.Composerwithvariant="conversation"(#770).
- New options:
CalendartakescustomViews(#763),display: "marker"andaccessibleDetail(#754).- Menu sections take
layout: "row"(#754), andDropdownlink rows take anaction(#751). FileDropzoneandChatComposer.fileSelectiontakechoose(#749).Lightboxtakes 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). PdfPreviewstays loading until the PDF has loaded, and anautoLoadpreview requests nothing in a browser without an inline PDF viewer (#767).- A renewed
VideoPlayeraddress resumes through a#t=fragment (#768). - Charts no longer read closed props during cleanup (#783).
- The
@k2b/ssrpeer range now includes 0.15.
- Dialogs and floating windows get their own error boundary. A dialog whose content throws while it opens shows the notice and resolves
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>threwStale 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/ui0.19.0 and@k2b/cloud0.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_storeis too small, NATS refuses the streams withinsufficient storage resources availableand 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
mailfrom@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.