Reference
Changelog
What changed in each version, newest first. The version that the server runs is at the foot of the Help panel in the app.
v2026.10.0
- A published dashboard ends when the dashboard does. Moving a published dashboard to the trash, an admin closing it to private, and deleting its owner's profile each take the published copy down and free its storage. Restoring from the trash does not publish it again. A dashboard given to a new owner stays published at the same address. Every published page links Report this page to the instance's contact, where the instance names one.
- An examples page. On an instance that publishes, an admin writes an examples page that links published dashboards. Visitors open it at
/examples, and the landing page links it. - The server keeps its heavy work inside its memory. Queries and chart pictures reserve memory before they start. When the server is full, a new one waits a few seconds, then gets a message that the server is busy and to try again shortly, in place of a crash that stops everyone's work.
- The sign-in pages match the rest of the site. The sign-in page, its error pages and the page that connects an agent take the paper look, in light and dark.
- A new mark: a cream sheet in blue ink, in the browser tab and on the home screen.
- Fixed — Sign in asked a signed-in person to sign in again. A person who is already signed in now goes straight to the app. Try signing in again and use a different address still open the sign-in page.
- Fixed — a shape with a label of the wrong type broke the editor, and some shapes did not fill their block.
- Fixed — a server run started from the app could fail while the server was busy. It now waits for its turn and for the run to finish.
- Fixed — a tab that published or unpublished a dashboard showed itself a notice about its own change, and a publish from another tab offered an Undo that could not work.
- Fixed — a branded instance's landing page named the instance as the tool. It now calls Arkush the tool and the instance the place that runs it.
- Fixed — an agent's `preview_transform` answer cut at its row limit did not say so. It now names the limit.
- Operations — the base compose file sets a 4 GB memory limit, and the service sizes its heavy work from it. At boot the service reads the container limit, keeps 1 GB for itself, and lets queries and chart pictures share the rest. The boot logs one
memory_budgetline, andmemory_budget_smallwhen the limit leaves room for less than one query. On a host with less than 4 GB, lowermem_limitin the instance override below the host's memory. On a larger host, raise it to run more at the same time. The install guide's Capacity section has the figures. - Operations — the examples page has its own file and its own public path.
ARKUSH_EXAMPLES_FILEnames the file,/data/pages/examples.mdin the image, and an admin edits it in the app. The page answers only whereARKUSH_ALLOW_PUBLISHINGis on. An identity proxy in front of the service must let/examplesthrough with no sign-in. - Operations — a Cloudflare zone in front of the service can hold the published pages. Set
ARKUSH_CLOUDFLARE_ZONE_IDandARKUSH_CLOUDFLARE_PURGE_TOKEN, with a token that has only the Cache Purge permission on that zone, and an httpsARKUSH_PUBLIC_URL. Every act that publishes, ends or replaces a published copy then removes its addresses from the zone at once, and Cloudflare holds a published page for five minutes. The zone needs a cache rule for/p/that takes the lifetime from the service's headers and leaves the query string out of the cache key. The boot refuses half the pair. Unset, nothing changes. - Licence — the warranted list of destinations gained `api.cloudflare.com`. The service reaches it only when the operator sets the Cloudflare pair above, to purge published pages. The annexes of the agreements are updated with the complete list.
v2026.9.1
- Fixed — the image of v2026.9.0 was never published. The image scan found a critical flaw in
proxy-addr, a library that the agent connection's dependencies carry, and stopped the publish. This release updates it to the fixed version, and carries everything v2026.9.0 lists below. An install moves from v2026.8.0 straight to v2026.9.1.
v2026.9.0
- Sign in with GitHub, with a code from your email, or with a passkey. Every Sign in button opens one page that lists the ways in the server offers, in the order the operator chose. An email code is eight digits, works once, for 10 minutes. After a sign-in, the app offers once to add a passkey, so the next sign-in takes the device's fingerprint, face or PIN. The account menu's Passkeys panel lists, adds and removes them. The app never stores or asks for a password.
- Sliders and field choices that charts read. A slider param holds a number between two bounds, and charts, measure formulas and block titles read it by name:
sum(revenue) * (1 + growth), or{growth}in a title. A field-choice param is a dropdown of columns and measures, and a chart channel or a scorecard set to Follows it changes its breakdown or its metric, with the axis title and the number format. The rail panel that lists them is now Controls. - Charts plot measures by name. A chart channel can name a measure such as ROAS, and the chart computes the ratio per category correctly. The field picker offers measures. A chart shape the computation does not handle shows a message that says why, never a wrong number.
- Filter from any chart. A click on any mark or legend entry filters the dashboard, Ctrl, Cmd or Shift adds a value, and a click on empty space clears. A drag across a date or number axis filters to a range, and a scatter plot takes a box. The clicked chart fades what is outside the filter. With the keyboard, Tab reaches a chart, the arrow keys walk its marks, Enter filters and Escape clears. The chart form lists every field a click or a drag can filter by.
- Version history reaches back 90 days. Each meaningful change is its own version, and repeated moves of one block merge into one. A refresh, a delivery or a comment no longer makes a version. Older versions thin by age: every save for 15 minutes, every change for 2 days, then the last state of each day. The list groups versions by day, and a version's link carries its number, such as
?version=42. - A broken block no longer breaks the dashboard. A save that would store a block no reader can draw is refused, with the block, the field and the fix named, from the canvas, the Source panel and agents alike. A block that fails to draw shows its own message, and the rest of the dashboard draws.
- Read a Cloud SQL PostgreSQL database. On an org install whose operator registers a database, a datasource can run SQL on it as a registered service account, in a read-only session, with no stored password.
- An empty dashboard shows only the steps you can take now: upload a file, connect BigQuery, use a shared datasource, and connect an agent until one is connected.
- The help is written for arkush.app. It opens on how a dashboard works, and adds two pages: what arkush is and is not, and the directions it may take. A section that only an org install offers says so under its heading.
- The app shows a page when something fails. A drawing error offers Reload page, an address with no page shows Page not found, and animation stops when the system asks for reduced motion.
- Smaller changes. The Connect an agent panel lists all eight agent clients. Resting the pointer on a column name in the chart spec editor shows the column's profile. Side panels carry headings that screen readers can jump between. The app has a new mark: an A4 sheet with an eight-point star.
- Fixed — a click on a line chart filtered to the wrong date. A click on a line, an area or a daily bar now filters to the date under the pointer, and a field a chart transform made writes no filter instead of a guessed one.
- Fixed — the first save of a new dashboard closed the open datasource editor.
- Fixed — a dashboard owner whose address has capitals was treated as someone else on the sharing panel, the lists, the Users page and when publishing.
- Fixed — a server-side fetch could reach an address other than the one it checked. A fetch for a web-address datasource or for an agent's sign-in details now connects only to the address it checked.
- Fixed — a failed service-account token named no cause. The message now names it, and Verify on the service account shows the command that fixes it.
- Agents — `get_dashboard` answers a short YAML outline of the params, the datasources one line per column, and the blocks with their index. The new
partsargument returns any part of the document in full, andparts: ["/"]returns the whole document as before.get_sandboxandget_shared_datasourceanswer the same compact column lines.list_databasesis new.create_dashboardtakes only format version 5, and every tool refuses an argument it does not take and names the ones it takes. Refusals name the failing operation, the blocks and the fix, and a dashboard an agent creates takes the house theme.validatereports a click filter on a field a chart transform makes, and checks every slider bound and dropdown choice. - Operations — the state database moves to schema version 4. The steps add a column to the sessions table and rebuild the version archive: consecutive versions with the same content, such as refresh-only or comment-only saves, are merged into one. Before the steps, the service writes
state.db.before-v2beside the database, and the boot logs oneschema_upgradeline. To roll back, stop the stack, replacestate.dbwithstate.db.before-v2, deletestate.db-walandstate.db-shm, and start the previous image. Work saved after the upgrade is not in the copy. The steps are indeploy/README.md→ Versions and upgrades. The document format stays at version 5. - Operations — the service backs itself up while it runs. Every
ARKUSH_BACKUP_HOURS(24 by default) it copies the database and every stored file tobackups/in the state directory and keeps the newest two, with no stop of the stack.SIGUSR2to the container takes a backup at once, and0oroffturns the timer off. The state disk needs free space for two more copies of the state directory. - Operations — new sign-in settings, all optional. With
ARKUSH_SIGNIN_PROVIDERSunset and the Google client pair set, a server offers Google alone, as before. To offer more, list them inARKUSH_SIGNIN_PROVIDERS(google,github,email,passkey, in page order). GitHub needsARKUSH_SIGNIN_GITHUB_CLIENT_IDandARKUSH_SIGNIN_GITHUB_CLIENT_SECRET, andARKUSH_SIGNIN_GITHUB_ORGSlimits it to members of named organizations. Email codes need a mail relay inARKUSH_SMTP_HOST,ARKUSH_SMTP_PORT,ARKUSH_SMTP_USER,ARKUSH_SMTP_PASSWORDandARKUSH_SMTP_FROM, and the server sends at mostARKUSH_SMTP_MAX_PER_HOURcodes an hour (60 by default). The boot refuses a listed method without its settings, and settings for a method that is not listed.ARKUSH_PROXY_HOPS(1 by default) is the number of proxies in front of the service, so the sign-in limits count the client's real address: set it when more than one proxy forwards requests. - Operations — Google sign-in changes for some addresses on an allowlist of domains or addresses. Where
ARKUSH_SIGNIN_ALLOWadmits people by a domain or an address entry, Google signs a person in directly only for a Gmail or Google Workspace address. A Google account on any other address gets an email code first, and is refused where email codes are off. WithARKUSH_SIGNIN_ALLOW=*, Google sign-in works as before. Under a domain entry inARKUSH_SIGNIN_ALLOW, such as@example.org, an address with a+tagis refused with the plain address named, and an open session of such an address ends at its next request. - Operations — registered databases and new log lines.
ARKUSH_DATABASES_FILEnames the hand-edited database registry, and with no file nothing changes. The install guide's new section Read a Cloud SQL database lists the setup. The service logssnapshot_prune_failedandsnapshot_drop_failedwhen a save or a delete leaves stored rows behind. Server-side requests now sendUser-Agent: arkush/<version>. - Licence — the warranted list of destinations gained two hosts and two operator-set destinations. Where the operator lists GitHub sign-in, the service connects to
github.comandapi.github.com. The mail relay inARKUSH_SMTP_HOSTand the databases in the database registry are destinations the operator sets.MIT-0joins the accepted licences of dependencies. The annex of every signed agreement is updated with the complete lists. - Privacy — update the stored-data list, the retention periods and the cookies. The service now stores email codes (the typed address and a hash of the code, for at most an hour), passkeys (the email and a public key), its own backups (the newest two, so a deleted record lives on in a backup until that backup is replaced), and the database registry (addresses and certificates, no password). The secrets gain the GitHub client secret and the mail relay password. Version history is now kept at most 90 days and 50 MB per dashboard, in place of the newest 50 versions. New cookies:
arkush_signin_code,arkush_signin_passkeyandarkush_passkey_add. The browser keeps two new keys:arkush.passkeyOfferDismissed, andarkush.chunkReloadAtin session storage. Update each instance's privacy page to matchdocs/security-overview.md→ What is stored where, and for how long.
v2026.8.0
- Help moves to docs.arkush.app, with pictures on every page. The guides, the admin guide and the reference pages now live on one public site beside the install guide. Every page carries pictures cropped to what it explains and short silent clips of each act, in a light and a dark copy that follows your system setting. The Help links in the app, the error notices and the agent's error replies open the site in a new tab, and carry nothing about your server: not its address and not its version.
- A theme holds a light face and a dark face. A dashboard in a paired theme follows each viewer's light or dark mode. New dashboards open in the Arkush look on a paper grain, with Lifted to choose from, and the plain pair is now named Classic. A theme with one face keeps its look.
- The paper look reaches every page. The landing page, the legal pages, the install guide and the app's own pages take it. The account menu's Plain background setting turns the grain off in this browser, and a system set to more contrast turns it off too.
- Blocks resize from every side and corner.
- Clicking a bar in a date chart filters to that period.
- The Help panel holds links. It lists the guides and Connect an agent, and its foot names your server, its legal pages and its security contact.
- The landing page leads with Sign in with Google, shows an agent building a dashboard, and keeps the account-free door as a link.
- A public deployment can offer a waitlist for the features an org install sets up: Slack subscriptions, channel deliveries and server-side BigQuery. See the Operations item.
- Fixed — connector lines drew no line, only their arrowheads. Two separator lines and one text color were missing for the same reason.
- Fixed — charts drew dates on the reader's clock. A chart over calendar dates now shows the day the data states in every time zone, on the axis, in the tooltip and in a click that filters by date.
- Fixed — a dark view look over a light app left table text and scorecard numbers dark, and links in Administration were unreadable in dark mode.
- Fixed — version history started at the save before the newest. The list and its caption now lead with the newest save and name who made it, an agent included.
- Fixed — an empty Deliveries file could not be saved, though the editor said an empty file delivers nowhere. Every refusal of that file now says what to write.
- Fixed — messages that named no fix. A paused scheduled refresh names each datasource's cause and the act that lifts it, and a datasource with no name is refused at its Name field.
- Fixed — the paper grain stayed one size while the canvas zoomed.
- Operations — `/help` on an install now redirects to docs.arkush.app. Each
/help/…address answers a temporary redirect to the same page on the site, so bookmarks and older links still land. The image carries no help pages, and an install without internet access has no help. The path stays on the list of public paths an identity proxy lets through until the first release that is in a later year and at least ninety days after this one, and then leaves it. - Operations — one new optional variable, `ARKUSH_WAITLIST`. Unset, nothing changes.
trueturns on the waitlist, which stores a signed-in person's email address, the feature and the date instate.db. The schema version and the document format do not change. - Privacy — add waitlist entries to the stored-data list, where the waitlist is on. Each entry is one person's email address, the feature they joined the waitlist for, and the date. It stays until the person leaves the waitlist or deletes their profile. An instance that leaves
ARKUSH_WAITLISTunset stores none and changes nothing. Otherwise, update its privacy page to matchdocs/security-overview.md→ What is stored where, and for how long.
v2026.7.0
- Split a dashboard into pages with frames. A frame is a named sheet on the canvas: add one from the header's Add menu, name it, and drag blocks onto it. A reader selects Pages to read one frame at a time, moves between pages with the tabs or the arrow keys, and selects Present to show them fullscreen. The address holds the open page, so a copied link opens the same page. On a phone a dashboard with frames opens on its pages. A control can apply to every block on a frame, a published dashboard keeps its pages, and a scheduled send lists its blocks by page.
- The header's block palette names its blocks. Chart adds a chart in one click. Add opens a menu of every block type in three groups, Data, Content and Layout, and each type has one line that says what the block is.
- Bullet and combo charts have a form. In Properties, a bullet chart takes its Category, Actual and Target fields, and a combo chart its X axis, Bars and Line fields. A number field adds up per category, and its aggregate can be changed below the field.
- A shared datasource keeps one stored copy of its rows. A scheduled refresh replaces that copy and no longer archives another full copy with each version. The details page and Use in new dashboard work as before.
- Fixed — a dashboard refreshed from the app stored its rows in its version history. The rows now go to the snapshot store, as they do with a scheduled refresh.
- Fixed — copying a shared datasource with no stored data. The app and the agent tool
add_shared_datasourceboth refuse the copy, and tell the reader to ask the owner to refresh the datasource in the datasource library. Before, the agent tool made a copy with no data. - Operations — the first start takes old snapshot rows out of the database. A dashboard or a shared datasource that an earlier release stored with its rows inside moves them to the snapshot store, and every archived version drops its rows. The start takes longer once, and the boot logs one
inline_rows_shedline with the counts and the bytes freed. The database file keeps its size until you compact it. To get the space back, follow the install guide → Compact the database. The step needs free disk larger thanstate.db, and the service is down while it runs. The schema version and the document format do not change. - Privacy — add shared datasources to the stored-data list. A shared datasource is an entry with up to 50 archived versions and no snapshot rows, and its rows are one payload in the snapshot store. Archived versions of dashboards and shared datasources hold no snapshot rows. Update each instance's privacy page to match
docs/security-overview.md→ What is stored where, and for how long.
v2026.6.0
- An inbox tells you what happened on your dashboards while you were away. An entry appears when someone shares a dashboard with you or hands one over to you, starts a comment thread on a dashboard you own or replies in a thread you are in, and when a scheduled refresh or a subscription stops. The Inbox button in the header shows how many entries are new. An entry goes away by itself when its problem clears, and Done puts one away. The share dialog has a Notify checkbox, on for a person and off for a group.
- Articles put a written finding beside the canvas. An article is markdown text with charts placed in it, and each chart keeps the filters its author set when it was placed. When the data under an article changes, the article says the text may no longer match. Open Articles on the right icon rail. A reader uses Open in dashboard under a chart to explore from that point, and a published dashboard publishes its articles with it.
- Changes by other people show on an open dashboard. When another editor, an agent or the scheduled refresh changes the dashboard you have open, the change arrives in place and a notice names who changed what. Your own unsaved edits stay. While an agent works, the header shows Agent editing. A change from your own agent or your own other tab has Undo.
- A collision keeps your version. When you and someone else change the same part of a dashboard, the dashboard shows their version of that part and keeps a copy of yours in version history as Set aside. The notice offers Compare and Put mine back.
- Leave a dashboard shared with you. Leave is in the Share panel and in the Dashboards page's row menu. The owner gets no message. Transfer ownership now follows the instance's sharing setting, so on an instance where only admins share, only an admin hands a dashboard to a new owner.
- A CSV with uneven rows imports, and says what it changed. A row with fewer fields than the header reads with its missing cells empty, and a row with more fields loses the fields past the header. Before you create the datasource, a note counts those rows and names the file line of the first one. Before, such a file read as one column with no message.
- A time with a time zone imports as UTC. A timestamp such as
2024-06-15T12:30:00+02:00is stored as2024-06-15T10:30:00Z, in the browser and on the server alike, whatever the zone of the machine. - A stopped scheduled refresh shows in the Data panel. When the schedule refused a datasource or its run failed, the Scheduled refresh section names it. An editor reads why and runs it once to resume the schedule, and a viewer is told to ask an editor.
- A folded chart shows your field labels. On a chart that folds several columns into one, the axis, legend or header prints each column's label, and the value axis takes the format the folded columns share.
- Clearer chart forms. The field type reads Number, Category, Ordered and Time, as in the Data panel. Label rotation shows its angle. A format with nothing set reads "Automatic", and an override reads "set on this block". A new chart at its default size shows every chart type without a scroll.
- A control takes its field's name. A new control's title becomes the name of the field it filters. A title you typed stays.
- A file dropped on a read-only dashboard says how to add it. The notice names the way out: switch to Edit, go back to the current version, leave the sandbox, use a larger screen, or ask the owner for edit access.
- Fixed — a scorecard or a pivot value moved to a measure kept a hidden reducer. The reducer is now removed in the same edit.
- Fixed — a marquee drawn inside a large section selected the section. A section now joins the selection only when the marquee encloses it whole.
- Fixed — smaller issues. An empty category reads
(Blank)also on a chart channel with no written type. A filter chip on a block with no data says to run the datasource. Changing a control's type clears its current value and says so. View mode starts with no block selected. A value missing from the snapshot can be removed from a field's order. A refused address stays in the sharing field for you to correct. A sheet on an instance that reads no private sheet says to use Import. A reload of a dashboard with no unsaved edits shows the owner's newer save instead of an overwrite banner. A Google account with no Google Cloud project reads how to create one, and a refused query names the project it billed. - Operations — on a header-trust install that allows anonymous use (`ARKUSH_ALLOW_ANONYMOUS`), the front now also skips the app's own pages:
/dashboards,/d/<id>,/datasources,/files/<id>,/users,/adminand each administration area, beside/appand/api/me. A reload or a bookmark on those pages then opens the app. Print the list again withnode scripts/proxy-paths.mjs --anonymousand update the proxy configuration. An org install changes nothing. - Agents — `render_block` reports what Vega-Lite warned while it drew the chart, as
findingsin the shapevalidateuses. A call whose arguments the tool's schema refuses, or a call to an unknown tool, now answers with a refusal that carriesreason,fixerandhelp, with the codebad-request.validatenames an unknown mark inside a layer or a composition, and advises on a category label that the axis cuts short.render_blocktakesview, the id of a view in an article, to draw the chart with the filters that view keeps.patch_dashboardwrites articles undermeta.articles. On a server with no delivery destinations, the agent is told that scheduled sends are not set up. - Operations — the state database moves to schema version 2. The step adds one column,
kind, to the version archive. Before the step, the service writes a copy of the database beside it,state.db.before-v1, and the boot logs oneschema_upgradeline that names the copy. To roll back, stop the stack, replacestate.dbwithstate.db.before-v1, deletestate.db-walandstate.db-shm, and start the previous image. A version set aside after the upgrade is not in the copy. The steps are indeploy/README.md→ Versions and upgrades. The document format stays at version 5. - Operations — new log events.
version_set_aside,inbox_mute,inbox_unmuteandprofile_unreadableare new, andrefresh_tickcarriessetAsidesDropped. Revoke all and Delete profile also remove the person's inbox. The install guide's event table lists every field. - Privacy — the version history keeps set-aside versions. When two people edit the same part of a dashboard at the same time, the working copy of the editor whose edit lost is kept in the history as a set-aside version, with no snapshot rows, and never becomes the dashboard. A dashboard keeps up to 10 set-aside versions beside its 50 archived versions, and each one is deleted after 30 days. Update each instance's privacy page to match
docs/security-overview.md→ What is stored where, and for how long. - Privacy — add the inbox to the stored-data list. The service stores each person's inbox entries and the dashboards they muted. An entry stays for 90 days. An entry about a failing scheduled refresh keeps why it stopped: the reason can name the address of the person whose run the schedule acts for, and it can quote a warehouse error, and it shows only to people who may edit the dashboard. Revoke all and Delete profile remove a person's inbox. Update each instance's privacy page to match
docs/security-overview.md→ What is stored where, and for how long.
v2026.5.0
- Every dataset opens on its columns. The Data workspace shows one tile per column: its type, how many values are missing, its distribution or top values, and its key numbers. A click on a value filters and sorts the rows, and one step charts any column. Filters and sorts there never change the dashboard.
- An import shows its data before anything is saved. Every way in — a dropped file, the Data panel, Replace data — stages the file in the Data workspace with its column profiles and the row grid, and Replace data lists the columns added, dropped or retyped.
- Hover a field to see its data. A field on a chart, a table name in the SQL editor or a datasource in Add data shows its column line on hover or focus, with a way into the workspace.
- The Data workspace has its own address, and it opens read-only for viewers and on phones. Shared datasources and uploaded files open there too, and a shared datasource's SQL is changed where it lives.
- The chart spec editor opens in one step from Properties. Inside a formula it completes column and function names and marks errors. It keeps the compact Vega layout, also on paste, and shows Vega-Lite's own warnings.
- Every error says who can fix it. An error message now links Who can fix this to its entry on the new help page, Error messages, which says what happened, who can fix it — you, the owner, an admin, or the operator — and what to do.
- An empty category reads `(Blank)` everywhere you read it: a chart's axis, legend and facet header, a table cell, a pivot key and the Color popover. A missing number still reads
–. - A chart that cannot draw says why. An editor reads the fix, and a viewer reads that an editor of the dashboard can fix it. A field the datasource lacks reads
(missing)on the encoding shelf. - Smaller editing changes. Refresh on a datasource row runs in place and reports by a notice. A note has a small form for its title and its text. A color swatch takes a hex value and offers the theme's colors, and a color channel with no colors set offers Set colors per value. Edit as JSON moves to the foot of the Properties form. The encoding shelf moves the canvas so that it never covers the chart it edits.
- Fixed — a CSV saved on Windows imports with its accented characters intact, dates that a transform makes read as dates, and the SQL editor in the Data workspace shows a whole query.
- Fixed — the Findings panel showed the previous dashboard's numbers after a switch, and a double-click on a chart title near the canvas's left edge opened the shelf instead of renaming the chart.
- Operations — the logs name people by pseudonym. No log line holds an email address: each person is
u_and 16 hex characters, the same on every line of one instance. A usage dashboard or a log view that readsuseras an email shows pseudonyms after the update. An admin finds a person's pseudonym under Administration → Users, anddocker exec <container> node /app/scripts/pseudonym.mjs <email>prints it on the host. - Operations — a new key file, `log-secret`, beside `state.db`. The first boot mints it. Keep it in the backup: without it, the lines after a restore name each person by a new pseudonym.
- Operations — `ARKUSH_LOG_LEVEL` is new and optional.
info, the default, writes every line.errorwrites the error lines and the failure events. The base compose file passes it through, so copy the compose file out of the new image as the upgrade steps say. - Operations — the logs are easier to read. A failure that the scheduled sweep repeats writes its first line, then one line an hour with a count, and one line when it clears. Each agent tool call writes one line with the tool and its outcome, never its arguments. The install guide's new Monitoring sections cover reading the logs, sharing one request id with a proxy, an optional route that sends the lines to Google Cloud, and every event with its fields. No schema step and no document format change: the state database stays at schema version 1, and the document format stays at version 5.
- Agents — a refusal's `reason` is an error code, and a refusal also carries `fixer` and `help`.
fixernames who can fix the situation, andhelpis the path of its entry on the Error messages page. The old reasons map to codes:forbidden→not-shared,no-edit-access,not-yours,not-a-save, or the run check'sservice-account-denied,no-service-account,not-signed-in,service-account-setup;no-bigquery,no-sandboxes,no-sa-registry,no-file-store,no-fetch-hosts,no-credential→not-configured;no-such-part,no-such-block,no-such-param,missing-datasource→not-found;missing-inputs→no-snapshot;local-sql,bq-sql,bq-not-found→sql-error;invalid-sandbox→invalid-doc;bad-op,bad-id,bad-url,bad-param-value→bad-value;bq-auth,impersonation-failed→service-account-setup;bq-permission→warehouse-permission;bq-bytes-billed→bytes-billed;bq-network,bq-http,http,network→upstream-unreachable;bq-timeout,render-budget→run-timeout;no-file-binding,no-url-binding→wrong-source;missing-file,bad-file-entry→file-missing;file-not-shared→not-shared;empty-source→file-not-readable;host-not-allowed→address-not-allowed;private-address,insecure-redirect→address-refused;unauthorized→source-not-readable;empty-spec,not-renderable,no-flows,external-data→render-failed. A catalog read of a table that does not exist answersnot-found. - Privacy — add the log key to the stored-data list. The service stores one more item: the log pseudonym key, a file beside the database. Log lines now hold pseudonyms and no email addresses. Update each instance's privacy page to match
docs/security-overview.md→ What is stored where, and for how long.
v2026.4.0
- A viewer builds their own charts in a sandbox. On a dashboard you can view but not edit, the header's switch reads View | Sandbox. In Sandbox, the dashboard's blocks lock, and you add charts, tables, notes and derived views over the data the dashboard already holds. Copy to sandbox on a chart's menu gives you a copy to change. The sandbox runs no warehouse query and never changes the dashboard. You and the dashboard's editors see it, and nobody else.
- Editors adopt a chart from a sandbox. The Sandboxes panel lists every sandbox on the dashboard. An editor opens one and uses Add to dashboard on a chart, or Replace on a chart that began as a copy of a dashboard chart. The chart brings the derived views it reads, and its owner sees a notice at their next open. Dashboard settings carries "Let viewers build their own charts in a sandbox", on by default.
- Every filter is in one Filters panel. The panel beside the canvas lists every param of the dashboard with its control, also a param that only chart clicks set. Viewers open it too.
- A dashboard with many charts opens faster. A resize now re-fits only the chart that changed, and a dashboard draws each chart once when it opens, where it drew each chart twice before.
- Revoke all also removes the person's subscriptions. In Administration → Users, Revoke all now ends the agent grants, the browser sessions and the subscriptions of the account in one act. Before, a person who left kept receiving their subscribed dashboards on an instance whose sign-in list names no one person. The confirmation says so.
- A session ends 90 days after its sign-in. A session still renews while in use, up to 90 days after the person signed in. Then the person signs in again.
- Seven smaller findings from a security review are closed. Among them, the agent consent page names the host that the approval returns to.
docs/security-overview.mdholds the current facts. - Fixed — a finding's jump to its block left an off-screen block out of view. The jump now pans the canvas to the block.
- Operations — a person who signed in more than 90 days ago signs in once more after the update. Nothing to set.
- Operations — offboarding has one more step. When a person leaves, use Remove all sandboxes under Users after Revoke all. A person's sandboxes stay until then, so that editors can still adopt a chart from them. The install guide's offboarding steps include it.
- Operations — the image runs Node 24.21. No schema step and no document format change: the state database stays at schema version 1, and the document format stays at version 5.
- Agents — four sandbox tools.
get_sandboxandpatch_sandboxread and change the caller's own sandbox over a dashboard.list_sandboxeslists the sandboxes on a dashboard for its editors, andadopt_blockmoves a sandbox chart into the dashboard.run_datasource,preview_transformandrender_blocktakesandbox: trueto work over the caller's sandbox. Apatch_dashboardthat renames or removes a datasource a sandbox chart reads answerssandboxWarningandsandboxesBroken. - Privacy — add sandboxes to the stored-data list. The service stores one more kind of data: a sandbox, one person's own charts and derived views over one dashboard, with the rows of those views. A sandbox stays after its person leaves until an admin removes it. Browser sessions now end at most 90 days after the sign-in. Update each instance's privacy page and terms to match
docs/security-overview.md→ What is stored where, and for how long.
v2026.3.0
- Operations — read this before you update: a person uses a service account through Service Account User, and Token Creator no longer admits a person. Neither image reads the other role, so grant the new role before you update and remove the old one after. Before you update: on each registered account (Administration → Credentials lists them), list the policy with
gcloud iam service-accounts get-iam-policy <account> --format=json, and give everyuser:,group:ordomain:member that holdsroles/iam.serviceAccountTokenCreatorthe roleroles/iam.serviceAccountUseron the account itself, for examplegcloud iam service-accounts add-iam-policy-binding <account> --member="user:<email>" --role="roles/iam.serviceAccountUser". Skip the runtime identity of the service: it keeps Token Creator and its policy-read role. After the new image starts and a person sees the account under Runs as, remove Token Creator from each member you moved, withremove-iam-policy-bindingand the same member. A grant on the project of the account does not count. The install guide's section "Upgrade from a release before 2026.3.0" holds these steps in order, and a person you do not move gets a refusal that names the missing role. - Operations — optional: grant a team once through a Google group. A Google group that holds Service Account User on a registered account now admits its members on every deployment, whichever groups backend it runs, and the group needs no registration in the app. To use group grants, enable the Cloud Identity API (
cloudidentity.googleapis.com) in the project that holds the runtime identity of the service, and have a Google Workspace super admin give that identity the Groups Reader admin role in the Admin Console. Do this before the first group grant: without it, a group grants nobody, and the refusal names the step. To move a team through a group at this update, do this first, then grant the group in place of each person. The install guide's section "Named service accounts" has the commands. A deployment whose policies name no group sends no new request. - A Google group can give a team its access to a service account. An admin grants the role to the group once, and a new colleague gets access by joining the group in Google.
- A datasource that runs as an account you cannot use now shows the way back. Its Runs as field offers Your own Google account, and the note beside it says why the account does not run for you. Refresh and Refresh all name the same switch instead of sending you to an admin.
- Your access to a service account no longer lets you take its token home. A person who runs queries under a registered service account now needs the Service Account User role on that account. That role mints no token, so the person cannot read the data of the account outside the app. Only the service mints tokens. The install guide says where to keep the accounts, so that the role allows nothing else of use.
- A comment always carries the name of the person who wrote it. Every save writes a new comment under the account that saves it, from the app and from an agent alike. A comment already on the dashboard keeps its author. An agent that acts for you reads a comment as yours only when you wrote it.
- Import makes a copy that you own. An imported dashboard starts private, as a duplicate does. It drops the shares, the visibility, the maturity stamp, the scheduled sends and the comment threads of the file. It keeps the blocks, the datasources, the data, the tags, the contact and the refresh schedule. Import is not a backup.
- An open dashboard answers its repeated requests faster. The checks an open tab repeats — the version check, the data downloads, who else is here, the live updates — decide access from a stored summary of the dashboard and no longer read the whole dashboard each time. On a large dashboard a check falls from tens of milliseconds to a fraction of one. The first start after the update reads every dashboard once to build the summaries, which takes a few seconds on a large instance.
- The sharing settings of an uploaded file are checked when you save them. A save with an unknown visibility value or a malformed share is refused, and the message names the value. A file saved before this check still opens.
- The health endpoint names the build.
/healthzalso answersrevision, the full commit that the image was built from. On a deployment that tracks the main branch, compare it with the latest commit to see which build runs. A service that runs from source answers norevision. - The boot log says when an instance record cannot be read. If the stored name, logo and security contact cannot be read, the boot logs
instance_identity_unreadable, and/.well-known/security.txtanswers 404 until an admin saves the identity again. If the stored access settings cannot be read, the boot logsinstance_policy_unreadable, and the instance runs on the default access settings until an admin saves them again. - Agents — a comment you add with `patch_dashboard` or `create_dashboard` is written under the account your grant acts for, whatever
authorit carries. A warehouse refusal names the Service Account User role, and a refusal read by an admin prints the command that grants it on the account.
v2026.2.0
- Publish a dashboard to the internet. The owner of a dashboard publishes it as a frozen copy at its own address, and anyone reads that copy with no account. The copy never queries the warehouse and holds no email address. It names the owner and each person who refreshed its data by a username. Each chart also gets its own page and a picture for link previews. Publishing again after a change updates the copy at the same address. Publishing is off on every instance until the operator turns it on.
- A Google Sheet shared by link reads with no sign-in. A sheet shared as "anyone with the link" reads in the browser with no credential, also for a visitor with no account. A private sheet reads with the person's own Google account, as before.
- Administration has seven areas, each at its own address. Users, Access, Credentials, Deliveries, Appearance, Pages and Configuration are separate pages under
/admin. Each deployment setting shows its effective value in the area that uses it. - Admins decide three more things about access. In Access, an admin sets who may connect an agent, who may take rows away as a file, and who may open a dashboard to everyone signed in. "Take rows away" covers Data as CSV, the export of a dashboard, the tables in a subscription, and the rows an agent reads. Each setting starts at its widest value, so an instance behaves as before until an admin changes it.
- One list shows what leaves the instance on a schedule. The Deliveries area lists every scheduled send and where it goes.
- Pick the service account in the datasource editor. A BigQuery datasource names its registered service account in the Runs-as field, and the table browser lists what that account can read. This applies where the operator registers service accounts.
- The table form starts from rows and values. Rows and Values come first in the form, and a field in Columns makes the table a crosstab. The separate pivot switch is gone.
- An instance open to any Google account offers only what it can do. On such an instance, only an admin shares a dashboard, and only its owner and an admin open the live dashboard. Everyone else reads the published copy, where the owner published one. The app hides each act the instance cannot perform, or says why it is not available.
- Help, About and the changelog are pages. The Help panel keeps the guides. About, the changelog and the list of what needs a source open as their own pages. After an update, a notice shows once with a link to what changed.
- Messages say what to do next. A refusal or an error in the app and from the server states what failed and an act the reader can take. It no longer names a setting or a person that the reader cannot reach.
- Scheduled sends carry only authorized rows. A scheduled delivery posts only rows that an authorized run wrote. Send now asks the rows setting of the subscriber, the same as the scheduled send. Before each run, a scheduled refresh or delivery checks that the person it runs for can still sign in.
- Keys moved out of the database. The session signing key and the snapshot attestation key live in the environment or in files beside the database. A copy of the database alone cannot sign a person in or vouch for a snapshot.
- Limits on large jobs. The server draws chart images in a separate worker with a memory limit and a time limit, and the server-side DuckDB engine has a memory limit. A job over its limit fails with a message and leaves the service running.
- Stricter checks on input. The server checks the sign-in return address, the upload quota, the registry files and the form of each request more strictly.
- The code editor moves to monaco-editor 0.56, with its bundled DOMPurify at 3.4.8.
- Fixed — Create accepted a datasource name the dashboard already used. Create now refuses that name. A table listing that timed out named a query that never ran, and now names its own time limit.
- Operations — every person signs in again once. At the first boot, the service deletes the old session signing key from the state database and makes a new one. With no
ARKUSH_SESSION_SECRETset, the key goes into a 0600 filesession-secretbesidestate.db. Set the variable to keep the key out of the state volume and out of every backup. Agent grants stay valid. - Operations — every scheduled refresh and delivery pauses once. The attestation key moves the same way, to
ARKUSH_ATTEST_SECRETor anattest-secretfile, and the attestation now also covers the rows. No attestation written before this release verifies. Run each server-run datasource once, from the app or withrun_datasource, and its schedule resumes. A delivery showsrows-unsealeduntil then. - Operations — publishing has its own switch and its own public path. Set
ARKUSH_ALLOW_PUBLISHING=truein the override'senvironmentblock to turn publishing on. Unset, no dashboard is published and every published address answers 404. Published pages are served under/p/. An identity proxy in front of the service must let/p/through with no sign-in. - Operations — the base compose file changed, and each release is signed in every registry. The compose file adds
ARKUSH_SESSION_SECRET,ARKUSH_ATTEST_SECRETandARKUSH_FETCH_HOSTS. The upgrade steps in the install guide, under Versions and upgrades, verify the image signature and copy the file out of the new image. The install guide is published at docs.arkush.app. - Operations — no schema step and no document format change. The state database schema stays at version 1, and the document format stays at version 5.
- Agents — `list_bigquery_datasets` takes `service_account`. The tool lists datasets as the named registered account, the same as
list_bigquery_tablesandget_bigquery_table. - Agents — an agent acts only on its own person's comments.
get_dashboardmarks each comment and reply that the grant's own person wrote withyours: true. The agent treats every other comment as a request to discuss with its author, and changes nothing for it. - Agents — `list_dashboards` shows who a dashboard is shared with only where the caller may edit it. Every row still carries the owner and the number of shares.
- Privacy — update the stored-data list and the note on BigQuery jobs. The service stores two new kinds of data: published copies, one per published dashboard, and usernames. The session signing key and the attestation key move from the state database to files beside it, or to the environment. Every BigQuery job the server runs carries labels that name the instance, the dashboard, the datasource, the person's address and how the run started. Google keeps those labels in its job history under its own retention. The browser stores one more key,
arkush.seenVersion. Update each instance's privacy page and terms to matchdocs/security-overview.md→ What is stored where, and for how long. - Licence — the warranted list of destinations gained two browser hosts. A browser that reads a Google Sheet by its link connects to
docs.google.comand*.googleusercontent.com. The annex of every signed agreement is updated with the complete list.
v2026.1.1
- Fixed — a dashboard whose snapshot the server stores as a Parquet file showed empty charts in the browser. Since v2026.1.0 a snapshot above 256 KB of rows is stored as a Parquet file. The browser's DuckDB engine reads that file through its Parquet extension, which it fetches from the app's own origin. The image carried only the JSON extension, so the fetch answered 404. The browser reported that the snapshot data could not be loaded, and every derived view over that datasource failed with a missing-table error. The image now carries both extensions. Nothing to run: reload the dashboard.
v2026.1.0
- Arkush leaves alpha. The landing page, the band under the header and the Help panel no longer warn that the document format changes without migration, because it no longer does: a dashboard saved under an earlier version opens in this one, the format migrates as the dashboard is read, and a change to the state database carries its own numbered step with a copy taken before it. The band now says where saved dashboards live and who can read them, once per machine. An exported copy (Dashboards → Export open dashboard) is still the one copy nobody else holds.
- Operations — versions are now year.release.fix, and this is the first tag under the scheme. The middle number counts feature releases in the year and restarts every January; the last number counts fix-only tags on a release. A fix tag carries fixes only and is safe to take blind: the release check refuses a fix tag on which any setting, format, schema, tool or public path moved. The number says how old an install is and nothing about compatibility — the Operations item on each entry carries that. A renamed or removed setting or tool keeps working until the first release that is both in a later year and at least ninety days after the announcement, and says so at boot or in the tool's description until then. No fix is backported: an install several versions behind moves forward through boot refusals that each name the next step. The
0.xtags stay as they are. - Operations — the state database carries a schema version, and this release stamps it. At boot the service reads SQLite's
user_versionand compares it with its own, which is 1. A database at zero — every install before this release — is read as version 1 and stamped, with no copy and no step. From here on, a release that changes a table names the new version in its Operations item, and before the first pending step the service writes a copy of the database beside it, named for the version it came from. Rollback across a step is that copy; within one version it is the previous image on the same state directory. A database a newer image wrote refuses this image's boot and names the lowest image that can read it. The upgrade and rollback steps are indeploy/README.md→ Versions and upgrades. - Fixed — two writes racing one stored payload name can no longer overwrite each other. The store publishes a snapshot payload or an uploaded file exclusively, so the first writer holds the name and a second writer carrying different bytes is refused, the way a later writer already was. A retry with the same bytes still succeeds.
- A warehouse datasource that names no account says so, everywhere it matters. Since every server-side BigQuery run names a registered service account, a datasource naming none stops running on the server. It now reports itself in the dashboard's findings panel with the fix, its schedule pauses with the reason in the tick's report instead of failing, and the admin page lists every such datasource on the instance under Datasources without an account until the list is empty. The Runs-as picker in the datasource editor also explains an empty list: when the server cannot read an account's policy, or the policy does not name you, the picker shows the sentence and the command that fixes it.
- A dashboard's data moves out of its file. A snapshot's rows used to live inside the dashboard document, which made every save write every row again and made a data-heavy dashboard slow to edit. The rows now live in the snapshot store — one payload per dashboard and datasource, beside the dashboard rather than inside it — and the document carries the payload's name. Nothing changes on screen: the app reads the rows when it opens a dashboard and puts them back when it saves, and a save that only moved a block uploads no data at all. The per-dashboard byte budget is gone with the rows it counted; the 50,000-row cap per snapshot is unchanged. A dashboard you already have moves the first time you save it, with no step from you.
- Operations — the two one-time migration tools are gone, and the image no longer carries them. The pre-state-plane importer and the data-bar sweep, the eight retired storage variables that refused a boot by name, and the two runbook sections that drove them are all removed. Nothing to do: a deployment that already runs this software runs it the same way. A move from the layout the state plane replaced is now a restore from backup.
- Operations — read this before you update: a misspelled setting now stops the boot. The environment has no spell-check, so
ARKUSH_ADMINin place ofARKUSH_ADMINSused to start normally and grant nobody the admin right. AnyARKUSH_name the service does not read refuses the boot and names the line. Check your env file againstdeploy/README.mdbefore the update rather than after. - Operations — a true or false setting takes the word, and nothing else.
ARKUSH_ALLOW_ANONYMOUS=1used to read as false, so a deployment did the opposite of what its env file said. Writetrueorfalse; anything else refuses the boot. The other three value grammars — lists, byte sizes and durations — are written down in the deploy guide for the first time and are unchanged. - Operations — every `ARKUSH_` setting can be mounted as a file. Point
<NAME>_FILEat a file and the service reads the value out of it, trimming the trailing newline, which is the shape Docker and Kubernetes secrets produce. Setting both keeps the direct value. The settings that already end in_FILEare unaffected. - Operations — the browser's billing project no longer falls back to the server's.
ARKUSH_BROWSER_BQ_PROJECTused to default toARKUSH_BQ_PROJECT. The common configuration therefore served the instance's own project as every signed-in author's default. An author with job-create rights there billed the organization, and one without them read the refusal as the app being broken. SetARKUSH_BROWSER_BQ_PROJECTif your authors should keep a default; unset, each author picks their own project in the app. - Privacy — the kinds of data the software stores changed, and every instance's privacy policy and terms state them. Two rows move and one is new. Dashboard documents no longer hold snapshot rows, and snapshot payloads are now one record per dashboard and datasource — held in the state database below 256 KB of rows and as a Parquet file above it; the same data sits in a different place. The new row is the upgrade copy of the state database: before a schema step, the service writes a copy of the whole database beside it, holding everything the database held, and keeps one such copy at most. Nothing leaves the host. Update the stored-data list on each instance's privacy page to match
docs/security-overview.md→ What is stored where, and for how long. - Operations — the state database stops growing at the old rate, and the file gives its space back only on a rebuild. Each dashboard's rows leave its document on that dashboard's next save, and the version archive stops carrying a copy of them per version. Nothing to run: the change happens as people use the app. On an install whose database is mostly snapshot rows, the freed pages are reused for new data and the file itself stays at the size it reached. To hand that space back to the disk, stop the service and run
VACUUM INTOagainst a new file, then put the new file in place — the same stop the backup already needs. - Agents — nothing changes at your end, and one error is gone.
get_dashboardstill answers a few sample rows per datasource: a snapshot now keeps its own sample beside the column profile, so the tool shows what a row looks like without the rows being in the document. Long strings in the sample are truncated the way profile values already were. Thesnapshot-budgeterror no longer exists —run_datasourceandadd_shared_datasourcecannot answer with it, because the byte budget it named is gone. - Agents and the Source panel — a snapshot's `parquet` field is now `payload`. It names the stored data and the storage picks that data's home by size, so the old name sent a reader looking for a Parquet file that most snapshots never become. The value is unchanged — the same name for the same rows. A dashboard saved before this release is migrated as it is read; nothing to run.
- Composite charts are charts — a spec that facets, repeats or concatenates now lives in an ordinary chart block. The separate
chart_compositeblock type is gone. Nothing changes on screen: a composition still keeps the per-view sizes declared inside it, and still opens the raw-spec editor rather than the encoding form, because every surface now reads the composition off the spec instead of off a stored word. The stored word could disagree with the spec, and an agent writing a faceted spec into a plain chart block produced exactly that: a chart that rendered wrong and reported an advisory instead. That state is now unrepresentable. - A datasource says where its rows come from and whose credential runs them, in two fields instead of one word.
executionanswered three questions at once — the place, the engine, and the identity — souser_oauthandservice_accountwere two words for one BigQuery query under two credentials, and a sheet read by a person's own Google spelled that a third way. Nowsourcesays where the rows come from and carries that source's own address or file, andrunsAssays whose credential the run spends: nothing for the deployment's own, a service-account email for a registered one,userfor the signed-in person's own Google. Nothing changes on screen or on a schedule. The four fields that only meant something next to one word —serviceAccount,file,url,credential— are gone, and the combinations that used to need a warning cannot be written down any more. - Operations — the document format moves to version 5, and no step is needed from you. Dashboards migrate as they are read, in memory, and the next save writes the new shape. There is no bulk rewrite and no pass at boot. A dashboard saved under this release does not open in an older one, which is the usual direction: roll back by restoring the state-directory copy the schema runner writes.
- Operations — every scheduled datasource pauses once, by design. The server's proof that a snapshot came out of an authorized run is bound to the datasource's shape and to who ran it, and both moved in this release, so the proofs written before it no longer verify. Run each credentialed datasource once — from the app, or with
run_datasource— and its schedule resumes. This is the same pause a rotation of the attestation secret causes. - Agents — the block-type vocabulary lost a member.
chart_compositeis no longer a block type, andvalidatenames it as a finding wherever it is written. Write a composition astype: "chart"with facet, repeat or concat in the spec. A document you send at version 1 is migrated for you. The authoring guide and thecreate_dashboard,render_blockandvalidatedescriptions say so. - Agents — `execution` is gone; write `source` and `runsAs`.
{ "execution": "service_account" }becomes{ "source": { "kind": "bigquery" } },"duckdb"becomes{ "kind": "derived" },"local"becomes{ "kind": "imported" }, andfileandurlkeep their names and take their binding inside the source —{ "kind": "file", "entry": "…" },{ "kind": "url", "href": "https://…" }. A named service account moves fromserviceAccounttorunsAs, andcredential: "user"becomesrunsAs: "user". A document you send at an older version is migrated for you. The initialize instructions, the authoring guide and everyvalidatemessage say so. - Google decides who may use a service account. Running a warehouse query under a registered service account used to need the analysts allowlist, or a place on a list kept in the app's own registry file. Both are gone. The app reads the account's own IAM policy in Google Cloud and asks whether that person holds Service Account Token Creator on it — the same rule the account already follows everywhere else in Google. Grant the role to a person and they may use the account here, with no allowlist and no restart. Derived views and file-bound datasources still need no account and no right beyond editing.
- Operations — read this before you update: every registered service account needs a second grant. The service now reads each account's IAM policy, and the impersonation grant it already holds does not carry that permission. Add
roles/iam.serviceAccountViewerfor the service's own runtime identity on every registered account. Until you do, a run naming that account refuses with a message that prints the exact command. - Operations — who may run is no longer configured in this application. The
mayRunlist on a registry entry is retired. An entry that still carries one is read as text and grants nobody; the file needs no edit. Grantroles/iam.serviceAccountTokenCreatorin Google Cloud to each person who may use the account. - Operations — an IAM outage longer than an hour pauses server-side runs. The service keeps each policy for one hour after the last successful read. Past that, a run naming a service account refuses and its schedule pauses until Google answers again. Derived views, file-bound datasources and addressed reads are unaffected — none of them asks Google who may spend an account.
- Agents — a service account is Google's to authorize.
run_datasourceon a datasource naming a service account, and the catalog tools browsing as one, now need the Service Account Token Creator role held by the person who approved your grant. The analysts allowlist does not reach a named account. The refusal names the missing role and the command that grants it. The authoring guide says so. - Licence — the warranted document format version moved from 1 to 5. Every signed agreement warrants this number in its annex.
- Licence — the warranted list of destinations gained one host. The service reads a registered service account's IAM policy at
iam.googleapis.com, to ask Google who may use that account. It sends the account's own address and nothing else, it happens only where the service-account registry is set, and an answer is kept at most an hour. Every signed agreement warrants the complete list in its annex. - The instance names itself from inside the app — its display name, its logo, and the security contact
/.well-known/security.txtpublishes now live on the admin page under Instance identity, where an admin edits them without a deploy. They are stored beside the theme library, so a backup carries them and a restore brings them back. - A registry turns on from the app, not from the env file — the groups membership file, the service-account registry, the destination registry and the onboarding questions each sit beside the state plane by default. An admin adds the first group, service account or delivery channel on the admin page and the feature starts working. Previously each one existed only if the operator had first pointed a variable at a path and restarted, while the editor for it sat in the app saying it was not configured.
- Operations — this release renames, removes and adds environment variables. Read this before you update. Every change is one edit to the env file. - Added, and required:
ARKUSH_IDENTITYstates how the deployment authenticates people —signin(the service signs people in over Google),proxy(an identity front sets an email header the service trusts), ordev(every tokenless caller is one address, localhost only). The service refuses to boot without it. Set the one that matches what the instance already does. Every other identity variable now belongs to exactly one mode, and one set under another mode refuses the boot by name:ARKUSH_SIGNIN_GOOGLE_CLIENT_ID,ARKUSH_SIGNIN_GOOGLE_CLIENT_SECRETandARKUSH_SIGNIN_ALLOWbelong tosignin,ARKUSH_AUTH_HEADER,ARKUSH_LOGIN_URLandARKUSH_LOGOUT_URLtoproxy, andARKUSH_DEV_USERtodev. - Added:ARKUSH_GROUPSpicks the groups backend —file(the default, the membership file admins edit in the app) orgoogle(the registry of Google group addresses). It replaces the rule that set one of the two file variables and left the other unset. - Renamed:ARKUSH_OAUTH_CLIENT_ID→ARKUSH_BROWSER_GOOGLE_CLIENT_ID,ARKUSH_GOOGLE_API_KEY→ARKUSH_BROWSER_GOOGLE_API_KEY,ARKUSH_GOOGLE_PROJECT_NUMBER→ARKUSH_BROWSER_GOOGLE_PROJECT_NUMBER, andARKUSH_BQ_BILLING_PROJECT→ARKUSH_BROWSER_BQ_PROJECT. The four are the browser's own Google coordinates, and the old names did not say so — one of them read as a pair with the server's sign-in client, which it never was. - Removed:ARKUSH_INSTANCE_NAME,ARKUSH_LOGO_URLandARKUSH_SECURITY_CONTACT— set them on the admin page after the update, once.ARKUSH_HANDBOOK_DIRis gone too; nothing set it. - A stale line refuses the boot. Every renamed and removed name above is refused by name at startup, and the refusal says what replaced it. An env file that keeps one does not start the service rather than running with a setting that means nothing — the failure this release exists to remove. - Changed meaning:ARKUSH_GROUPS_FILE,ARKUSH_GOOGLE_GROUPS_FILE,ARKUSH_SERVICE_ACCOUNTS_FILE,ARKUSH_DESTINATIONS_FILEandARKUSH_ASK_FILEnow only move a file that already has a place beside the state plane. An instance that sets one keeps working. An instance that sets none gets every registry, empty. A variable that names a path holding no file refuses the boot, because a stated location with nothing at it is a typo rather than an empty registry. - Every server-side warehouse query now runs as a named service account, so Google decides who may spend it. A datasource could leave the account blank and run under the deployment's own identity instead, which reads every dataset that identity was ever granted — and the analysts allowlist handed that whole reach to everyone on it. Blank is no longer a way to run: a datasource names an account an admin registered, and Google's own permissions on that account say who may use it. Derived views, uploaded files and addressed data are untouched, and a query under your own Google still runs in your browser.
- Operations — read this before you update: dashboards whose datasources name no account stop refreshing. For each one, set "Runs as" on the datasource to a registered account and run it once; the schedule resumes from there. Two consequences worth knowing: the analysts allowlist no longer opens the warehouse, so a person who should keep running queries needs the Service Account Token Creator role in Google Cloud instead — and the service's own identity now needs no BigQuery access at all, so you can take that grant off it.
- Agents — a warehouse datasource must name `runsAs`, and the catalog tools require `service_account`.
run_datasourceon abigquerysource with norunsAsanswersforbiddenand names the picker that sets one.list_bigquery_tablesandget_bigquery_tabletake the account as a required argument —list_service_accountsnames the registered ones, and per-account catalogs differ, so the answer is that account's view of the warehouse. Browsing under the deployment's own identity is gone. The authoring guide says so. - A schedule stops when the person behind it loses access. A scheduled refresh runs on somebody's authorization: the person whose run last blessed the datasource. That authorization used to outlive them, so a departed analyst's dashboards kept refreshing under an account they could no longer use themselves. The sweep now re-checks that person before every run, and pauses the datasource when Google no longer grants them the account. Removing someone in Google Cloud is what stops their scheduled spending, and it takes effect within the hour. The sweep's own report lists every datasource it stopped and why, under
unauthorized, naming what the person is missing and, where that is a Google role, the command that grants it back; each refusal also writes a log line naming the document, the datasource and the person. The dashboard itself still reads as scheduled until somebody acts on that report — a screen for it is not built yet. Anyone who may still run the datasource restarts its schedule by refreshing it once. - A snapshot says who refreshed it, and what it ran as. The two used to share one field, so a refresh under a service account showed the account's address where a person's name belongs — and left nobody to check when the schedule ran itself. Now the provenance line names the person, and the snapshot records the account beside it — which is what the server's refresh notice reports back and what agents read. A dashboard saved before this release is migrated as it is read.
- Agents — a snapshot answers `runsAs` beside `refreshedBy`.
get_dashboardandrun_datasourcereport the person the run was made by inrefreshedBy— your grant's consenting account, where you ran it — and the credential it spent inrunsAs, present only when the run named one. Previously a service-account run reported the account inrefreshedByand reported the person nowhere. - Operations — the stack names the image it runs, and every install sets one more line. The base compose file used to build the software from a source tree, which an install does not have. It now runs the image
ARKUSH_IMAGEnames, and compose refuses to start when that line is missing. Put the image and its version tag in the env file:ARKUSH_IMAGE=<image>:v<version>. Pin the version tag rather thanlatest, which moves to another version at the next recreate. The image carries the compose file itself, so an install that holds the image and the deploy guide holds everything it needs to start. To build from a source tree instead, add-f deploy/build.override.ymlto the compose command.