Security you can read
Security you can read: what ships, what does not, and how to report a problem
MyPerchi devices and the platform are built so the things that matter cannot be taken from you: passkeys first, a P-256 key generated on every device and never sent, pairing codes and Wi-Fi passwords that never reach our servers, secrets encrypted per record, signed firmware updates that roll back, and a threat model of 49 threats mapped to tests.
Accounts
How do you sign in?
With a passkey first: Face ID, Touch ID, Windows Hello or a security key, with user verification required on both registration and sign-in, so a PIN-less key is refused rather than merely discouraged, and a passkey sign-in already counts as two-factor. The universal fallback is an 8-digit emailed code; Google and GitHub sign-in are optional; passwords are off by default.
Email codes that cannot be stolen from the database
The 8-digit code is stored as an HMAC bound to its challenge id and lives 15 minutes; 5 wrong attempts kill the challenge and a code is consumed exactly once. The start endpoint answers 202 with constant response timing, so nobody learns whether an address has an account.
An authenticator app, if you want one
Optional TOTP with a replay guard and 10 recovery codes stored as SHA-256; the whole set regenerates at once. It adds a factor on top of email codes; a passkey never needs it.
Sensitive actions ask again
Removing a device, deleting a workspace, creating a token and similar actions require that you confirmed your identity in the last 10 minutes; a stale confirmation gets a 403 rather than a silent success, and the session token rotates when you confirm.
Hardened sessions
A __Host- HttpOnly Secure SameSite=Lax cookie whose token is stored only as SHA-256; 14 days idle, 90 days absolute; rotated every 24 hours and on every step-up; sign-out-everywhere revokes every session and closes live streams.
Cross-site protection
Sec-Fetch-Site and Origin checks plus an X-Mtx-Csrf header on state-changing requests; every redirect target is validated so a sign-in link cannot bounce you to another site. Token requests ignore cookies entirely.
Strict headers
The API answers with default-src 'none', nosniff and no-referrer; the dashboard uses a per-request nonce policy with strict-dynamic and frame-ancestors 'none'; HSTS is set for a year; Permissions-Policy blocks camera, microphone and geolocation.
Pairing and identity
How does a device prove it is yours?
Every Perchi and Perchi Matrix generates its own P-256 key on first boot, in a dedicated identity partition, and never transmits it; the server knows the device only by the SHA-256 fingerprint of its public key. Pairing proves you are in the room before it proves you own the device, and the 6-digit code you type is checked with a password-authenticated key exchange (SRP6a) on the device itself, so the code never crosses the network.
Pairing
- Bluetooth
- BLE Security 2 with SRP6a: one online guess per session, the code rotates after each failure, 10 failures per boot lock Bluetooth until the button is pressed
- Wi-Fi + code
- A WPA3 setup network whose passphrase is shown only on the real display, an 8-character code and a physical button press
- Wi-Fi password
- Travels inside the AES-256-GCM Bluetooth session or over WPA3 straight to the device; lives only in the wizard's memory; never stored on the server
- Claim tokens
- Hashed, 15 minutes, single use, bound to your session and the device fingerprint
- After pairing
- Every pre-claim call is body-signed; afterwards the device presents an ES256 token valid for at most 1 hour, and everything pushed to it (apps, values, notifications, commands, update offers) is a signed P-256 envelope bound to its id and ownership epoch
Ownership
- Already owned
- A second claim gets 409 unless the device's own signed reset counter advanced, which needs a physical factory reset; the previous owner is notified
- Transfer lock
- Optional: refuses any transfer until you allow it in the dashboard
- Bystanders
- Someone who types your display code first only moves the device to awaiting button, and your display shows a hint about the claimer
- No self-wipe
- Only a signed unclaim command or a physical reset erases credentials; an authentication error causes a retry, never a wipe
- Factory reset
- Really forgets your Wi-Fi: the credentials are held in RAM-backed storage and restored to defaults on reset or unclaim
Secrets and data
Where do your connected-account secrets live?
Encrypted per record. Each API key, OAuth token, webhook secret, authenticator secret and device command secret gets its own random data key under AES-256-GCM, wrapped by a keyring key that never sits in the database, with the ciphertext bound to its table, column, row and workspace so it cannot be moved or swapped. Nine such columns are sealed, a test in every build scans the raw database file and its write-ahead log for the plaintext, and a saved secret is masked on every read after that.
Tokens exist only as hashes
Access tokens are stored as SHA-256; generated tokens are shown once. The four kinds carry a CRC32 checksum so secret scanners recognise a leak, and 9 scopes bounded by the member's role are re-evaluated on every request. Seven actions can never be done with a token at all.
Workspaces are walls
Every workspace route answers 404 for a foreign id, so nothing leaks its existence, and a contract test probes 90 or more routes for it. The AAD binding on secrets is a second, cryptographic layer of the same isolation.
Logs never carry secrets
Eleven field names two levels deep, the authorisation, cookie and signature headers, and the code, state, token, key and signature query parameters are redacted; webhook path secrets are rewritten before they are logged. A snapshot test covers the sensitive flows.
Keys rotate without downtime
A re-wrap job walks the sealed columns in batches under a new keyring key with a written runbook. The firmware signing key has its own rotation path; the one key that cannot yet be rotated over the air is listed below.
The egress guard
What stops our server from being used against your network?
Every outbound request an app makes on your behalf, whether to GitHub, Razorpay or a JSON URL you typed into the builder, takes one path: https only, a host on that app's allow-list, unicast addresses only, and the name re-resolved on every single connection so a DNS answer cannot switch to a private address after it was checked. Redirects are re-validated at every hop and credentials are stripped the moment one changes origin.
- Bare, single-label,
localhost, metadata and.local,.internal,.lanstyle names are refused before any lookup; decimal, octal and hex IP tricks are normalised away before the unicast check ever sees them. - A total deadline and a byte cap counted after decompression bound every call, so a compressed bomb aborts early. JSON is depth- and key-counted before it is parsed and stripped of prototype-polluting keys afterwards.
- Request headers are allow-listed; host, cookie, forwarded and similar headers are always dropped. A per-host budget of 60 requests a minute keeps one runaway app from starving every other customer's calls to a popular service.
- The test suite runs a corpus of about 20 attack addresses (cloud metadata, loopback in every notation, mapped IPv6, wildcard DNS services) against the real egress policy of every app in the catalogue, and asserts that the catalogue is at least 40 apps long so the corpus can never silently run against nothing.
Updates
How are firmware updates protected?
Signed updates, staged rollouts, automatic rollback. The signing key never touches a server: a custodian signs each release manifest offline and the server stores the exact signed bytes. The device checks the signature, the key id, the key purpose and its expiry, the model, the version floor, the image size and free memory, and streams the SHA-256 while downloading, before it installs anything.
Built, never merged
A tagged release is built in a pinned toolchain that installs only public trust keys and refuses anything containing a private key. Artifacts are separate parts with checksums, never a merged image, so the identity partition is never in a file we distribute.
Signed offline
The manifest is signed by the fw-v2a key on an air-gapped machine and imported as a file. An operator publishes it from the admin console after the image magic, chip id, descriptor and size are validated.
Rolled out in stages
1 percent, then 10, 50 and 100, across dev, beta and stable channels. The rollout halts itself when the failure rate crosses a threshold (10 percent by default, after at least 3 finished targets) and alerts the platform admins.
Trial boot, then confirm
The new image must confirm itself with a successful authenticated connection or a grace timer; a reset before that returns to the previous image. After 3 crash boots the device enters safe mode: clock only, but still reachable and still updatable.
The container images behind the hosted service are built the same way: multi-architecture, with a software bill of materials and a provenance attestation you can verify, third-party images pinned by digest and every build action pinned by commit. Releases are dated on the changelog.
Local API
Is the local API safe to turn on?
On a network you trust, yes, with its limits stated plainly: the local API is off by default, is switched on only by a signed command from your dashboard, needs a token that is stored hashed on the display and works nowhere else, answers only to its own host name or IP address, refuses browser cross-site calls and caps itself at 30 requests a second. It is plain HTTP on port 80, so a bearer token is readable to anyone who can see traffic on your LAN.
Pending
What have we not done yet?
Each item names its status today and what it means for you. Nothing here is hidden in a footnote; when an item closes, it moves out of this list and into the changelog.
Secure Boot and flash encryption
The firmware images that ship today have no Secure Boot v2, no flash encryption and no encrypted key storage. A production build profile with them exists but is not qualified: the encrypted identity partition has not been verified on a real unit, the release firmware does not yet carry the hosted device domain, and the manufacturing boundary between secured and unsecured units is undecided.
What it means for you: Someone with physical access and tools can erase the board and claim it as a new device. They cannot reach your account, your data or your other devices with it, you are notified when a device you own is claimed, and the transfer lock refuses that claim until you allow it. Do not read a promise of tamper resistance into anything on this page.
Anti-rollback in hardware
Hardware anti-rollback (eFuse) belongs to the same production profile. Released images use a software minimum-version floor plus the bootloader's trial boot and rollback.
What it means for you: A signed older image could be installed on a device with physical access; it cannot be installed over the air, because the device refuses images below its version floor.
Over-the-air rotation of every key
The firmware understands a keyset update command; the server side that would send it is not built. The firmware signing key can be rotated today; the key that signs content envelopes cannot be rotated across the fleet without a firmware update.
What it means for you: A compromise of that content key would be fixed by a signed firmware update rather than a silent rotation. There is no evidence of any compromise; this is a gap in tooling, listed so you know it exists.
A third-party audit
None yet. The launch-readiness review that produced this list is internal, and the automated security suite runs on every change.
What it means for you: Read this page as a set of mechanisms you can check, not as a certificate. When an external assessment happens, its scope and date will be published here.
Round-the-clock alerting for the hosted service
One operational alert exists (a firmware rollout that halted itself). The alert-evaluation job is designed but not built; until then we run an external uptime check and look at the admin console daily.
What it means for you: No 24-hour monitoring claim, and no status page yet. Your device keeps its clock, timers, cached apps, local API and MQTT if the service is unreachable.
A purge job after account deletion
Deleting your account is a soft delete with every session revoked and a 30-day grace period announced by email. The job that erases the data at the end of that period is not built yet.
What it means for you: We do not print a purge date until the job exists. You can still revoke and scrub every connected account from the dashboard today.
Encryption on the local API
The display's local API is plain HTTP on port 80 of your LAN, off by default. Signed-request mode protects integrity, not confidentiality, and Home Assistant cannot use it.
What it means for you: In bearer mode, someone who can sniff your network can read the token. It works only for that display, only on the LAN, and you can rotate it at any time.
Client-side encryption of the backup stream
Nightly backup snapshots of the hosted service are encrypted before they leave the host. The continuous replication stream is not, and relies on the storage bucket's own encryption; the keyring never sits in the database.
What it means for you: A copied backup still needs the keyring to read any secret, which is what the raw-dump test proves; the stream itself is a defence-in-depth gap, not a plaintext-secrets gap.
Data minimisation
What does the device send, and how long do we keep it?
No camera, no microphone, no speaker. A device sends its identity (a public-key fingerprint and a 4-character short id), model, firmware version, reset counter and a capability report; hourly health aggregates such as free memory, Wi-Fi signal, frame rate and disconnects, kept 30 days; and the state needed to show your apps. Perchi adds focus-block events (started, paused, resumed, completed or partial) whenever it is online, never what you were doing and never timestamps finer than a minute in reports.
What never leaves the device
- Wi-Fi password
- Goes from the wizard to the device; never to the server
- Pairing code
- Checked on the device with SRP6a; the server never sees it
- Touch and phone-presence readings
- Raw readings stay on Perchi; only block events are reported
- Focus rooms and links
- Only a state: idle, working or done for the day; never what an app is showing, never content
- Record my Perchi clips
- Reconstructed from events the server already knows; there is no camera to record with
How long we keep things
- Sign-in IP addresses
- Full for 30 days, then truncated to the network prefix
- Sessions
- 14 days idle, 90 days absolute; rows deleted 30 days after expiry
- Device health telemetry
- 30 days, hourly aggregates only
- Device commands
- 7 days; secret arguments nulled once the device acknowledges
- App value history
- 90 days by default; app events 30 days
- Public-link messages
- Text removed after 30 days, row deleted after 90; visitor addresses stored hashed
- Share clips
- 30 days, then the file and its row are deleted
- Webhook deliveries
- 7 days
- Audit log
- 365 days, append-only
Threat model
What is in the threat model?
49 threats, each written down with its mitigation and the automated test that proves it: T1 to T20 cover the device and Bluetooth pairing, W1 to W29 cover the web platform. A guard test fails the build if any evidence file disappears or is skipped, and the security suite is a required step of every change, with no permission to continue on error. A sample of the table:
| Id | Threat | Mitigation |
|---|---|---|
| T2 | Active Bluetooth man-in-the-middle or a fake device | SRP6a pairing: one online guess per session; the browser aborts on a bad proof |
| T3 | Guessing the 6-digit pairing code online | Code rotates after each failure; exponential backoff; 10 failures lock Bluetooth until the button is pressed |
| T11 | Taking over a device someone else owns | Signed reset counter, transfer lock, notification to the previous owner |
| T19 | Wiping a fleet through faked authentication errors | Only a signed unclaim command or a physical reset wipes credentials; a 401 causes a retry |
| W8 | Reading another workspace's data by guessing ids | Tenant filter in every repository, 404 for foreign ids, secrets bound to the workspace by AAD; tested across 90 or more routes |
| W10 | Stealing connected-account keys from a database dump | Envelope encryption with the keyring outside the database; a test greps the raw file for plaintext |
| W12 | Server-side request forgery through an app's URL | The egress guard: https only, allow-listed hosts, unicast-only addresses, DNS re-checked per connection |
| W15 | Forging or replaying an incoming webhook | Per-provider signature verification, timestamp windows, delivery dedupe |
| W27 | Losing the device identity, or re-claiming a stolen unit, through a USB reflash | The USB installer writes separate parts and never a merged image, so the identity partition survives |
The full table is not public yet; the mechanisms it points to are the ones described on this page. Threat T14, a physical flash dump, is the one whose mitigation is still pending, which is why Secure Boot heads the pending list above.
Disclosure
How do you report a security problem?
Email security@myperchi.com. Tell us what you found, how to reproduce it, which part is affected (a device, the dashboard, the API, a public link) and, for a device issue, the model and firmware version. If you have a request id from an API response, include it. We reply by email, keep you informed while we fix it and credit you in the changelog if you want to be named.
Please do
Test against your own devices, accounts and workspaces. Send us proof-of-concept detail privately. Give us reasonable time to fix and roll out before you publish.
Please do not
Touch other customers' devices, data or public links; run denial-of-service tests against the hosted service; or ask a stranger's display to show something as a demonstration.
What you can expect
A human reply, a fix that ships as a signed firmware update or a dashboard release, an entry on the changelog, and your name on it if you want. There is no bounty programme today.
For anything that is not a vulnerability, such as pairing trouble or a warranty question, the support page and contact page are faster.
Questions about security
Do MyPerchi devices have a camera or microphone?
No. Neither Perchi nor Perchi Matrix has a camera, a microphone, a speaker or a battery. Perchi senses a phone resting on its back with capacitive electrodes, and those raw readings never leave the device; only block events (start, pause, resume, complete or partial, minutes and project kind) are reported, never raw readings. The dashboard itself sends a Permissions-Policy header that blocks camera, microphone and geolocation in the browser.
Do you see my Wi-Fi password?
No. During Bluetooth pairing it travels inside an AES-256-GCM session straight from the browser to the device; on the Wi-Fi + code path it goes over the device's own WPA3 setup network to the device's own page. It lives only in the setup wizard's memory while you pair, is never stored in the dashboard, and a factory reset erases it from the device as well.
Can someone on my network control the display?
Not unless the local API is on, and it is off by default: only a signed command from your dashboard turns it on, per display. When on, every call needs the token or a signed request, the display answers only to its own host name or IP address, and browser cross-site calls are refused. It is plain HTTP on your LAN, so turn it on only on a network you trust.
Can a stolen device be reused?
With tools and physical access, yes: today's units have no Secure Boot or flash encryption, so a board can be erased and claimed as new. What a thief cannot do is get into your account, your data or your other devices. Claiming a device that is already owned needs a physical factory reset that the device counts and signs, you are notified if it happens, and the transfer lock in the dashboard blocks the claim until you allow it.
Are firmware updates safe?
Yes: every update is signed, staged and reversible. A new image runs as a trial boot and must confirm itself through a successful authenticated connection, or a grace timer; a reset before that confirmation makes the bootloader return to the previous image. Before any of that, the device checks the signature, key id, key purpose and expiry of the update, its model and version floor, and streams the SHA-256 while downloading. Updates also go out in stages, 1, 10, 50 and 100 percent, and halt themselves if failures appear.
Has MyPerchi been audited?
Not yet. What exists is a threat model of 49 threats, 20 on the device and Bluetooth side and 29 on the web side, each mapped to an automated test that a guard test keeps honest, plus an internal launch-readiness review whose open items are listed on this page. When an external assessment is completed, its scope and date will be published here.
Where are my API keys stored?
Encrypted per record with AES-256-GCM: every secret gets its own random data key, wrapped by a keyring key that lives outside the database, and the ciphertext is bound to its table, column, row and workspace so it cannot be moved. After you save a key it is masked on every read and decrypted only inside the worker that needs it, and a test in every build scans the raw database file and its write-ahead log for the plaintext.
How do I report a vulnerability?
Email security@myperchi.com with what you found, how to reproduce it and, for a device issue, the model and firmware version. We reply by email, keep you informed while we fix it and credit you in the changelog if you want to be named. Please do not test against devices, accounts or data that are not yours, and give us reasonable time before you publish.
What each device does day to day is on the Perchi and Perchi Matrix pages; the API, webhooks and local API that these protections sit under are on the developer page.
Batch 2 - estimated 2 to 3 weeks, no payment today
Reserve a device that is on your side
Perchi is ₹2,999 incl. GST (MRP ₹3,499). No subscription needed.
No payment today. We email a checkout link when your unit is bench-tested; current estimated wait 2 to 3 weeks from the day you reserve. Cancel any time before then.