Skip to content

The kitchen can actually make a sound - #848

Merged
sridharkalaibala merged 4 commits into
developfrom
fix/the-kitchen-can-actually-make-a-sound
Sep 17, 2026
Merged

sridharkalaibala merged 4 commits into
developfrom
fix/the-kitchen-can-actually-make-a-sound

Conversation

@sridharkalaibala

@sridharkalaibala sridharkalaibala commented Sep 17, 2026

Copy link
Copy Markdown
Contributor

Three faults, each of which alone was enough to keep a kitchen silent. All three were found on a customer machine with the current build installed, by reading files and a log rather than by listening, which is itself the point of the third change.

1. The CSP blocked every sound in the product

Every noise this product makes is a WAV synthesised in the main process and handed to the page as a data: URL: the new order alarm and the kitchen ting both. Nothing is shipped as a sound file, nothing is read off disk and nothing is fetched, which is the right design for a till that has to work with the internet unplugged.

helmet's default directives do not name media-src. Without one declared it falls back to default-src 'self', and 'self' does not include data:, so the browser refused all of them. From the customer machine's log, 240ms after a QR order arrived:

Loading media from 'data:audio/wav;base64,UklGRhYTAABXQVZF...' violates the
following Content Security Policy directive: "default-src 'self'". Note that
'media-src' was not explicitly set, so 'default-src' is used as a fallback.
The action has been blocked.

That is the new order alarm being silenced. The same mechanism carries the kitchen ting.

Fixed with one directive, media-src 'self' data:. data: only and media only: a data: URL cannot reach the network and cannot execute, so the worst a bad one does is make a noise.

2. There was no way to turn it on

The setting was real and the IPC was real. The only way to reach either was to open the developer console on the machine at the pass and type:

posnic.kitchenCall.set({ ting: true, speak: true })

Nobody was going to do that, so nobody did. A setting nobody can find is not a setting.

Two switches now sit on Core Settings beside the auto-focus ones, stored the same way: per machine, saved immediately, because somebody flipping this is standing next to the speaker waiting to hear the difference. They hide themselves outside the desktop app, where there is no bridge behind them, because a dead control is worse than an absent one.

3. There was no way to test it without a real order

Which is why both of the faults above went unnoticed for the feature's whole life, and why they were eventually found by reading a log.

A Test button on the same block goes down the road a real ticket takes: the same switches, the same tone, the same sentence, the same window. A test that took a shortcut would be the thing that passes while the kitchen stays silent. It says why when nothing happened, because "I pressed it and nothing happened" is where this feature has spent its life.

Not in this PR, but needed with it

The installed build carries a stale frontend bundle. frontend/static/script/js/core/kitchen-call.js is the half that plays the tone and speaks, and it is correctly listed under dashboard in pages_css_js_map.json on main, but the shipped dashboard.20f9c50d.js does not contain it: no kitchenCall, no speechSynthesis. Building the frontend from main produces a bundle that does (verified locally). So whatever staged that exe did not run prepare:frontend. The next installer has to be built through npm run build, which runs it and rejects stale artifacts.

Tests

tests/a-sound-the-browser-will-actually-play.test.js

  • helmet still does not provide media-src itself, so the line stays load-bearing
  • the CSP declares media-src and allows data:
  • it does not grow a remote origin or a wildcard

tests/the-kitchen-switch-has-somewhere-to-live.test.js

  • both switches exist on a page somebody can open, and are two switches rather than one
  • they show what the machine is actually set to, rather than always rendering off
  • the Test button reaches the main process, and the handler uses the real announcer and the real switches
  • the block hides itself where there is no bridge

The CSP test was verified failing on the parent commit and passing on this one.

dev to main: the two kitchen switches and the packaging completion
The tones are synthesised in the main process and handed to the page as
data: WAV URLs, so that a till with no internet and no sound files on
disk can still make a noise. helmet's defaults do not name media-src, so
it fell back to default-src 'self', and the browser refused all of them.

Found in a customer machine's log, where the new order alarm had been
mute since the header was tightened:

  Loading media from 'data:audio/wav;base64,...' violates the following
  Content Security Policy directive: "default-src 'self'". Note that
  'media-src' was not explicitly set, so 'default-src' is used as a
  fallback. The action has been blocked.

This is the failure that looks like success: a console warning nobody
reads, and a symptom of silence in a room where silence is expected. The
kitchen announcement could not have been heard whatever else was fixed.

data: only, and media only. A data: URL cannot reach the network and
cannot execute, so the worst a bad one does is make a noise.
The announcement shipped with no way to turn it on. The setting was real
and the IPC was real, and the only way to reach either was to open the
developer console on the machine at the pass and type

  posnic.kitchenCall.set({ ting: true, speak: true })

Nobody was going to do that, so nobody did, and a finished feature sat
switched off for its whole life. A setting nobody can find is not a
setting.

Two switches on Core Settings, beside the auto-focus ones and stored the
same way: per machine, saved immediately, because somebody flipping this
is standing next to the speaker waiting to hear the difference. Hidden
outside the desktop app, where there is no bridge behind it - a dead
control is worse than an absent one.

And a Test button, which is the point of the block. Until now the only
way to learn whether a kitchen would hear anything was to send it a real
order, which is why both faults found in this feature were found by
reading a log rather than by listening. It goes down the road a real
ticket takes: the same switches, the same tone, the same sentence, the
same window, and it says why when nothing happened.
@sridharkalaibala sridharkalaibala changed the title Every sound the app makes was blocked before anyone heard it The kitchen can actually make a sound Sep 17, 2026
Owner: "make female voice. more casual than machine voice. indian accent?"

A SpeechSynthesisVoice carries a name and a language and nothing else,
so the only way to ask for a particular voice is by name. The plain
language match picked Ravi, the male half of the Windows en-IN pair,
purely because Windows enumerates him first. Both voices satisfied
"en-IN", nothing was wrong, and the wrong voice came out anyway.

Now the accent is chosen first and the voice within it second, so an
Indian woman beats a British one: the dish names are the whole point,
and a name not on the list falls through to whatever that language does
offer rather than failing.

Delivery off the flat baseline as well, and kept close to normal on
purpose. Past about 1.15 a synthesised voice stops sounding relaxed and
starts sounding like a cartoon, which a kitchen laughs at twice and then
ignores.

Verified on this machine under Electron: it now picks Microsoft Heera,
English (India), where it picked Ravi before.
@sridharkalaibala
sridharkalaibala merged commit 6938346 into develop Sep 17, 2026
10 checks passed
@github-actions github-actions Bot added the ready for QA Merged to develop and live on develop.posnic.io - anyone can test it label Sep 17, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Merged to develop. Anyone can test this - you do not need write access.

Try it at https://develop.posnic.io, or run it yourself:

git fetch origin develop && git checkout develop
npm install && npm --prefix api install
npm run dev   # then http://localhost:3000

When you have tested it, say what you did and what happened, and set
QA passed or QA failed. If you cannot set labels, just comment -
a maintainer will.

Reporting that something is broken is as useful as fixing it. It is
better found here than by a shopkeeper.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready for QA Merged to develop and live on develop.posnic.io - anyone can test it

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant