medOS ultra

Ever Sovereign Wallet (B2B2C)

B2B2C sovereign wallet integration: migration + edge function + dashboard panel.

13 min read diagramsUpdated 2026-04-16docs/integrations/ever-wallet.md

Status: Phase 0 shipped (migration + Edge Function + dashboard panel). Phase 1 pending first-customer config.

What it is: Integration between medOS-ultra (this repo) and the Ever Sovereign Wallet desktop/stick app deployed one-per-room in partner nursing homes. Residents speak “Hey Ever, call a nurse” and the request lands in the medOS nursing home dashboard for staff to acknowledge.

Regulatory framing: Supplementary voice channel, NOT a primary nurse-call system. Physical pull-cords / bedside buttons must remain in place. Wellness device, not a medical device.


1. Architecture

┌──────────────────────────────────────────────────────────┐
│  Nursing home room                                       │
│  ┌────────────────────────────────────────────────────┐  │
│  │  Ever Sovereign Wallet (Mac/Win/Linux or Android)  │  │
│  │  • Per-resident health wallet identity             │  │
│  │  • "Hey Ever" voice assistant                      │  │
│  │  • call_nurse + request_help voice actions        │  │
│  │  • FacilityAlertService (HMAC-signed webhook POST) │  │
│  └───────────────────────┬────────────────────────────┘  │
└──────────────────────────┼───────────────────────────────┘
                           │ HTTPS POST
                           │ X-Ever-Signature: sha256=<hmac>
                           ▼
┌──────────────────────────────────────────────────────────┐
│  medOS-ultra (this repo)                                 │
│                                                          │
│  ┌─ Supabase Edge Function: ever-wallet-alerts ──────┐   │
│  │  1. Lookup device by wallet_address              │   │
│  │  2. Verify HMAC-SHA256 against device secret     │   │
│  │  3. Upsert into ever_wallet_alerts (dedup)       │   │
│  │  4. Update device.last_seen_at                   │   │
│  └──────────────┬───────────────────────────────────┘   │
│                 │                                        │
│                 ▼                                        │
│  ┌─ Supabase tables ────────────────────────────────┐   │
│  │  ever_wallet_devices — paired sticks             │   │
│  │  ever_wallet_alerts  — alert rows + lifecycle    │   │
│  │  ever_wallet_alerts_audit — append-only audit    │   │
│  │  (RLS + realtime publication enabled)            │   │
│  └──────────────┬───────────────────────────────────┘   │
│                 │ realtime subscription                  │
│                 ▼                                        │
│  ┌─ NursingHomeDashboard.tsx ───────────────────────┐   │
│  │  <EverAlertsPanel />                             │   │
│  │  • Priority-sorted pending alerts                │   │
│  │  • [I'm coming] / [Resolved] buttons             │   │
│  │  • SLA countdown badges                          │   │
│  │  • Updates Supabase row via everAlertService     │   │
│  └──────────────────────────────────────────────────┘   │
└──────────────────────────────────────────────────────────┘

What this integration intentionally does NOT do:

  • No NestJS/Moleculer service changes
  • No NATS event publishing
  • No Kaigo billing engine changes
  • No touching of the hospital admission workflow
  • No modification of existing nursing_home_residents, nursing_home_rooms, or kaigo_facilities tables

The entire integration lives in 5 files: one migration, one Edge Function, one frontend service, one React panel, plus a one-line mount in NursingHomeDashboard.tsx.


2. Files

File Purpose LOC
web/supabase/migrations/20260416_ever_wallet_alerts.sql 3 tables + RLS + triggers + realtime publication ~290
web/supabase/functions/ever-wallet-alerts/index.ts HMAC-verified webhook receiver ~210
web/src/services/ever-wallet/everAlertService.ts Frontend Supabase wrapper (list / ack / resolve / subscribe / pair) ~260
web/src/containers/nursing-home/EverAlertsPanel.tsx Realtime React panel mounted in the nursing home dashboard ~380
web/src/containers/nursing-home/NursingHomeDashboard.tsx One-line mount above the existing Care Alerts section +3

3. Data Model

ever_wallet_devices

One row per paired stick. Persistent — represents the hardware bound to a room.

Column Type Notes
id UUID PK
wallet_address TEXT UNIQUE Ever wallet EdDSA/Ethereum address
device_label TEXT Human label (“Room 204 stick”)
facility_id UUID FK → kaigo_facilities Nullable (staging devices)
resident_id UUID FK → nursing_home_residents Nullable
room_id UUID FK → nursing_home_rooms Nullable
hmac_secret TEXT 32-byte hex shared secret, rotated at pairing
ack_callback_url TEXT Optional reverse webhook for “nurse coming” TTS
status enum active / paused / retired
paired_at, paired_by timestamptz / UUID Audit
last_seen_at timestamptz Updated on every alert
metadata JSONB { residentName, roomLabel, language, ... }

FK constraints are added via DO $$ ... $$ blocks that check for target table existence — safe against partial deploys.

ever_wallet_alerts

One row per alert, full lifecycle.

Column Type Notes
id UUID PK medOS-generated
client_alert_id TEXT Wallet-generated (alert_1744838400_a1b2c3d4) — used for dedup
device_id UUID FK
facility_id, resident_id, room_id UUID Denormalized for RLS + fast queries
resident_name_cached, room_label_cached TEXT Snapshot for display
kind enum call_nurse / request_help
priority enum emergency / urgent / routine / request
subtype TEXT For request_help: water/food/bathroom/comfort/entertainment/family_call/other
reason TEXT LLM intent summary of what the resident said
raw_payload JSONB Full original webhook body (audit)
status enum pending / ack / resolved / timeout / cancelled
created_at, received_at timestamptz Wallet creation vs. medOS ingestion time
ack_at, ack_by, ack_by_name Who acknowledged
resolved_at, resolved_by, resolved_by_name Who resolved
sla_deadline_at timestamptz Computed via trigger: emergency=60s, urgent=180s, routine=900s, request=900s
reverse_ack_* Phase 1 — for sending “nurse coming” back to the wallet

Natural key: UNIQUE (device_id, client_alert_id) — wallet retries become no-op upserts.

ever_wallet_alerts_audit

Append-only log written by the ever_wallet_write_audit trigger. Never manually edited. One row per lifecycle event.


4. Webhook Contract

POST {SUPABASE_URL}/functions/v1/ever-wallet-alerts

Headers

Content-Type:          application/json
X-Ever-Alert-Id:       <client alert id>
X-Ever-Alert-Priority: emergency|urgent|routine|request
X-Ever-Alert-Kind:     call_nurse|request_help
X-Ever-Signature:      sha256=<hex hmac-sha256 of body, keyed with device.hmac_secret>

Body

{
  "alert": {
    "id": "alert_1744838400_a1b2c3d4",
    "kind": "call_nurse",
    "priority": "urgent",
    "subtype": null,
    "reason": "pain in my hip",
    "createdAt": "2026-04-16T14:30:00.000Z",
    "residentId": "0xabc...",
    "roomId": "room-204",
    "status": "pending",
    "deliveryAttempted": false
  },
  "facilityId": "sompo-tokyo-shinagawa",
  "facilityName": "SOMPO Care Tokyo Shinagawa",
  "residentName": "Tanaka",
  "deliveredAt": "2026-04-16T14:30:00.200Z"
}

alert.residentId must match a wallet_address in ever_wallet_devices. The device’s hmac_secret is used to verify the signature.

Responses

Status Body Meaning
200 { ok, alertId, status, slaDeadline } Alert stored, dashboard subscribers notified
400 { error } Malformed payload / missing fields
401 { error } Missing or invalid HMAC signature
403 { error } Device is paused/retired
404 { error } Device not paired (unknown wallet_address)
405 Non-POST method
500 { error } Internal error

HMAC spec

signature = "sha256=" + hex(hmac_sha256(device.hmac_secret, request_body))

The body used for signing must be the exact bytes sent, before any parsing. The Edge Function reads req.text() first, then verifies, then parses.


5. Deploying Phase 0

5.1 Run the migration

# Against local dev stack
cd web
supabase db push
# Or directly:
psql "$SUPABASE_DB_URL" -f supabase/migrations/20260416_ever_wallet_alerts.sql

Verify:

SELECT table_name FROM information_schema.tables
  WHERE table_name LIKE 'ever_wallet%';
-- Expect: ever_wallet_devices, ever_wallet_alerts, ever_wallet_alerts_audit

5.2 Deploy the Edge Function

cd web
supabase functions deploy ever-wallet-alerts

The function needs two env vars (already set in Supabase project config by default):

  • SUPABASE_URL
  • SUPABASE_SERVICE_ROLE_KEY

5.3 Pair a test device

Option A — via frontend (Phase 1 UI, not yet built):

import { everAlertService } from '@services/ever-wallet/everAlertService';

const device = await everAlertService.pairDevice({
  walletAddress: '0x1234abcd...', // from the stick
  deviceLabel: 'Room 204',
  facilityId: '<kaigo_facilities.id>',
  residentId: '<nursing_home_residents.id>',
  metadata: { residentName: 'Tanaka', roomLabel: '204', language: 'ja' },
});
console.log('HMAC secret — paste into stick:', device.hmac_secret);

Option B — via SQL (Phase 0 manual):

INSERT INTO ever_wallet_devices (
  wallet_address, device_label, facility_id, resident_id, room_id,
  hmac_secret, metadata
) VALUES (
  '0x1234abcd...',
  'Room 204',
  '<facility-uuid>',
  '<resident-uuid>',
  '<room-uuid>',
  encode(gen_random_bytes(32), 'hex'),
  '{"residentName": "Tanaka", "roomLabel": "204", "language": "ja"}'::jsonb
)
RETURNING id, hmac_secret;

The returned hmac_secret is displayed once — save it; it goes into the stick’s facility config.

5.4 Configure the stick

On the Ever Sovereign Wallet desktop app (DevTools console):

await window.everWallet.facility.setConfig({
  enabled: true,
  webhookUrl: 'https://<project>.supabase.co/functions/v1/ever-wallet-alerts',
  webhookSecret: '<hmac_secret from step 5.3>',
  facilityId: '<kaigo_facilities.id>',
  facilityName: 'Customer Facility Name',
  residentId: '<wallet address matching ever_wallet_devices.wallet_address>',
  residentName: 'Tanaka',
  roomId: 'room-204',
});

5.5 End-to-end test

  1. On the stick, enable voice and say “Hey Ever, call a nurse, I’m in pain”
  2. Expect in DevTools: [Facility] ALERT CREATED log + webhook delivery
  3. Expect in medOS dashboard (http://localhost:5173/nursing-home/dashboard): new alert appears in the Voice Alerts panel within ~1 second via realtime subscription
  4. Click “I’m coming” on the nurse dashboard
  5. Alert status transitions pending → ack
  6. Click “Resolved” — alert fades out after 4s

6. Gaps (Phase 1)

  • [x] Netflix-style pairing flow (Phase 1 SHIPPED). Manual SQL + DevTools copy/paste is gone. Replaced with a short-TTL (90s) pairing-code handshake + Proton §5 fragment-key return path. See §9 below for full details.

  • [ ] Reverse ack channel. When nurse clicks “I’m coming”, medOS should POST to device.ack_callback_url so the stick TTS says “Tanaka-san, the nurse is on her way.” Implemented as a Postgres trigger + pg_net, or a second Edge Function invoked by the frontend after ack.

  • [ ] Facility-scoped RLS. Current policies are permissive for authenticated — tighten once we integrate with the medOS staff auth claim system and know the exact JWT shape.

  • [ ] SLA timeout sweep. A cron job that flips pending alerts past their sla_deadline_at to status=timeout and fires an escalation notification.

  • [ ] Pairing UI. A Facility Setup screen for care managers to provision new sticks without SQL.

  • [ ] Audio capture attachment. 10s pre/post ring buffer from the wallet, uploaded to filestore on call_nurse alerts for liability + nurse tone-of-voice context.

  • [ ] Worklist integration. Create a medical-worklist task per alert so acked alerts appear in the nurse’s main task queue (Phase 2).

  • [ ] Push notification fanout. Hook into services/messaging/notification/ so LINE Works / FCM / SMS fire on emergency priority.

  • [ ] Family portal mirror. Family members see alert history (sanitized) in the family portal.


7. Operational Notes

  • Audit log: ever_wallet_alerts_audit is append-only. Never DELETE or UPDATE rows — they’re a legal record. Retention policy TBD with first customer (30/90/forever).
  • HMAC secret rotation: To rotate, generate a new secret, UPDATE ever_wallet_devices.hmac_secret, then update the stick config. There’s a brief window during which in-flight alerts may fail signature verification — accept that, or add a hmac_secret_previous column in Phase 1 for graceful rotation.
  • Multi-facility queries: The frontend service passes facilityId as a filter on every query. Phase 1 enforces this at the RLS layer via JWT claims.
  • Realtime channel naming: ever_wallet_alerts:${facilityId} — one channel per facility keeps subscriber counts bounded.
  • Supabase cost: Each alert = one Edge Function invocation + one row insert + one realtime broadcast. At 100 alerts/day per facility, cost is negligible (well under $1/month per facility at current Supabase pricing).

9. Phase 1 — Netflix-style secure pairing (SHIPPED)

The Phase 0.5 manual SQL + DevTools flow is replaced by a full self-service pairing system with cryptographic separation from edh-central infrastructure.

Files shipped in this repo

File Purpose
web/supabase/migrations/20260417_ever_wallet_pairing_codes.sql ever_wallet_pairing_codes table (90s TTL, single-use) + audit table + extends ever_wallet_devices with paired_via, device_ephemeral_pub, paired_with_code_id columns + cleanup function
web/supabase/functions/ever-wallet-pair/index.ts Deno Edge Function: verifies code → generates HMAC → wraps with nacl.box → consumes code → returns wrapped secret + stick config
web/src/containers/nursing-home/EverDevicePairingDialog.tsx MUI v7 admin dialog with 6-digit-code + quick-setup tabs, 90s countdown, auto-regeneration, realtime transition to “Paired successfully”

Operator setup (one-time, per Supabase project)

# 1. Generate server static X25519 keypair (Deno one-liner)
deno run -A -r - <<'EOF'
import nacl from 'https://esm.sh/tweetnacl@1.0.3'
import { encodeBase64 } from 'https://esm.sh/tweetnacl-util@0.15.1'
const kp = nacl.box.keyPair()
console.log('EVER_PAIR_STATIC_PRIV=' + encodeBase64(kp.secretKey))
console.log('EVER_PAIR_STATIC_PUB='  + encodeBase64(kp.publicKey))
EOF

# 2. Set as Supabase secrets
supabase secrets set EVER_PAIR_STATIC_PRIV="<from step 1>"
supabase secrets set EVER_PAIR_STATIC_PUB="<from step 1>"

# 3. Apply the migration
supabase db push  # or psql with 20260417_ever_wallet_pairing_codes.sql

# 4. Deploy the function
supabase functions deploy ever-wallet-pair

After this one-time setup, the admin dialog at `` can generate codes that pair sticks in ~5 seconds with no DevTools involvement.

Handshake

1. Admin clicks "Pair new device" → dialog calls:
     INSERT INTO ever_wallet_pairing_codes { facility_id, resident_id, room_id, hints }
     → 6-digit code, expires_at = NOW() + 90s

2. Admin reads code to stick operator (or shares the "Quick setup" link)

3. Stick enters pairing screen, generates fresh ephemeral X25519 keypair:
     const eph = nacl.box.keyPair()
     const deviceId = "ever-pair-" + random(16)
   POSTs to ever-wallet-pair:
     { code, devicePub: eph.publicKey, deviceIdentifier: deviceId, deviceLabel }

4. Edge Function:
   a. Looks up active code (expires_at > NOW() AND consumed_at IS NULL)
   b. Generates fresh HMAC secret (crypto.getRandomValues(32))
   c. Inserts ever_wallet_devices row (wallet_address = deviceId, hmac_secret)
   d. Wraps HMAC with nacl.box(hmacSecret, nonce, devicePub, STATIC_PRIV)
   e. Consumes code atomically (race-guarded — on loss, rolls back device row)
   f. Returns { deviceId, wrappedHmacSecret, serverPub, nonce, functionUrl, … }

5. Stick unwraps with nacl.box.open(..., eph.secretKey)
   Persists to its OWN electron-store file (ever-facility-pair.json) encrypted
   with its OWN KDF salt. Zeros + discards eph.secretKey.

Security properties

  • Forward secrecy on HMAC delivery: TLS compromise does not leak the HMAC. Attacker needs either the ephemeral private key (stick RAM, <1s lifetime) or the server static private key (Supabase secret, never on the wire).
  • Proof of possession: only the stick that sent the ephemeral pubkey can unwrap the response.
  • 90-second TTL + single-use: leaked codes are useless after the window closes or after first consumption.
  • Bound at generation: code is pre-scoped to {facility_id, resident_id, room_id} so a leaked code can’t be redirected.
  • Race-guarded consumption: if two devices try to claim the same code simultaneously, only one wins; the loser’s orphan device row is rolled back.
  • Revocable: flip ever_wallet_devices.status = 'retired' in the admin UI and the next alert from that stick is rejected with 403.

Quick-setup flow

The “Quick setup” tab in the admin dialog renders the same 90s-TTL code inside an ever-pair://v1?u=<b64>&c=<code>&f=<b64>&r=<b64> URL. The stick’s pairing screen has a matching “Paste setup link” tab that parses the URL and pre-fills both the Supabase URL and the code — one paste, one click.

This is a UX accelerator, not a second security model. The code inside the URL has the same 90-second TTL and single-use semantics as a manually-entered code. A leaked URL is useless after the window closes, even though it “contains” all the pairing info.

Completely separate from edh-central

The wallet-side pairing module (src/main/facility-pairing.ts in ever-sovereign-wallet) is architecturally and cryptographically isolated from edh-central:

  • Own electron-store file: ever-facility-pair.json, not shared with ever-sovereign-config.json
  • Own KDF salt: ever-facility-pair:${machineId}, mathematically independent from edh-store’s ever-sovereign:${machineId} SHA-256
  • Own ephemeral keys: fresh nacl.box.keyPair() per pairing, NOT the user’s ethers.Wallet key
  • Own device identity: random ever-pair-<16b> id, NOT the user’s Ethereum wallet address
  • Zero imports from edh-store, sync, wallet, clinical, or any other B2C module
  • Own IPC namespace: facility-pair:* separate from facility:* and sync:*

A compromise of edh-central master keys reveals nothing about the facility HMAC secret; a compromise of the facility HMAC reveals nothing about edh-central-encrypted personal health data. The two trust trees share only the OS machine ID as a seed, and apply independent KDF salts to it.


The wallet side lives in Ever-Healthcare-OMA/ever-sovereign-wallet. Relevant files:

  • src/main/facility.tsFacilityAlertService (webhook sender)
  • src/renderer/src/components/VoiceAssistant/types.tsCallNurseAction, RequestHelpAction
  • src/renderer/src/components/VoiceAssistant/useVoiceFunctionCaller.ts — localized executors (en/ja/zh/th)
  • src/renderer/src/components/AlertBanner.tsx — on-stick toast
  • docs/B2B2C_FACILITY_ARCHITECTURE.md — wallet-side architecture doc
Ask Anything