chore(bootstrap): import plum-side edits missed in initial commit

- @hardware/docs/IDEAS.md: card-form-wallet component layout sections
- platform-api/entities/index.ts: import 9 peer-* entities that already
  existed in tree but weren't wired

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
natalie 2026-05-18 08:30:46 -07:00
parent 1b719e1fd7
commit 40534d4874
2 changed files with 332 additions and 11 deletions

View file

@ -58,6 +58,136 @@ Edge / back view:
optional pogo pads for factory recovery only optional pogo pads for factory recovery only
``` ```
### Component layout — does it all fit, does it balance?
Card footprint: standard ID-1 / credit-card, **85.6 × 54 mm**, target
**34 mm thick**. Budget every external surface (front, back, edge)
plus an internal Z-stack of ~5 layers. Heavy / area-hungry components
get **stacked in Z** rather than fighting for X-Y real estate.
**Front face** (member-facing, calculator UX):
```
┌──────────────────────────────────────────────────────┐
│ ◉ cocotte ▒ status LED │ ← top status row
│ ┌────────────────────────────────────────────────┐ │
│ │ e-ink display (~50 × 25 mm) │ │ ← display
│ │ balance · peer handle · amount · code │ │
│ └────────────────────────────────────────────────┘ │
│ │
│ ┌───┐ ┌───┐ ┌───┐ ┌─────┐ │
│ │ 7 │ │ 8 │ │ 9 │ │ ÷ │ │
│ ├───┤ ├───┤ ├───┤ ├─────┤ │ ← 3×4 numeric +
│ │ 4 │ │ 5 │ │ 6 │ │ × │ │ 1×4 operator
│ ├───┤ ├───┤ ├───┤ ├─────┤ │ membrane keypad
│ │ 1 │ │ 2 │ │ 3 │ │ │ │
│ ├───┤ ├───┤ ├───┤ ├─────┤ │
│ │ . │ │ 0 │ │ ⌫ │ │ + │ │
│ └───┘ └───┘ └───┘ └─────┘ │
│ │
│ [ MENU ] [ ◉ SEND ] [ = / CONFIRM ] │ ← action row
└──────────────────────────────────────────────────────┘
```
Front is the most pixel-budget-constrained surface. Membrane keypad
sits directly on the main PCB; e-ink display is laminated above. No
free area on the front to spare for the LED strip or piezo — those
move to edge / back.
**Back face** (the radio + power face — everything stacks here):
```
┌──────────────────────────────────────────────────────┐
│ serial · cocotte ······························· │
│ ┌─────────────────────────────────────────────┐ │
│ │ │ │
│ │ NFC antenna loop ── outer ring │ │ ← NFC + Qi coils
│ │ ┌─────────────────────────────────────┐ │ │ stacked
│ │ │ │ │ │ (flex multilayer,
│ │ │ Qi v2.1 receiver coil │ │ │ same X-Y zone)
│ │ │ ( ~38 × 38 mm spiral ) │ │ │
│ │ │ │ │ │
│ │ └─────────────────────────────────────┘ │ │
│ │ │ │
│ │ NFC tap zone (this whole region) │ │
│ └─────────────────────────────────────────────┘ │
│ │
│ ▣ recovery QR (etched, flush) ◡ pogo indent │ ← smooth shallow
│ (46 pads) │ indent, flush pogo
└──────────────────────────────────────────────────────┘
```
The Qi coil (~38 mm class) and the NFC antenna loop share the same
X-Y real estate because they're on **different flex layers** and run
at totally different frequencies (Qi ~100 kHz, NFC 13.56 MHz). Stack
order is engineered so neither blocks the other; this is the standard
trick on every card-form wallet that ships Qi today.
**Edge** (switch + status):
```
top edge : ───── perimeter LED strip wraps all 4 sides ─────
left edge : ◀── [ LOCK · SEND · RECV ] ──▶ ◉ piezo port
░░░ (toggle pulse LED under detent)
right edge : status LED · cellular antenna feed point
bottom edge: (clean — no exposed metal, no port)
```
Charging is wireless (Qi), so the bottom edge has no port and stays
clean. Cellular antenna is a meandered trace along the inner side of
the card edge — antenna real estate is what makes the perimeter
ring useful for two things at once (radio + LEDs).
**Internal Z-stack** (top of card → back of card, ~34 mm total):
```
┌──────────────────────────────────────────┐ ← 0.3 mm — front shell
1 │ front shell + membrane keypad overlay │
├──────────────────────────────────────────┤
2 │ e-ink display module │ ← 0.5 mm
├──────────────────────────────────────────┤
3 │ main PCB — MCU · modem · secure │ ← 0.6 mm
│ element (EAL6+) · iSIM/eSIM · BLE/NFC │ PCB + components
│ controller · PMIC · piezo step-up · LED │
│ drivers · supercap (SMD) │
├──────────────────────────────────────────┤
4 │ Li-Po pouch (~40 × 45 × 1.0 mm, │ ← 1.0 mm
│ ~150 mAh, centered) │
├──────────────────────────────────────────┤
5 │ multilayer flex — Qi coil + NFC loop │ ← 0.4 mm
│ + perimeter LED strip + piezo flex tail │
├──────────────────────────────────────────┤
6 │ back shell w/ pogo indent + QR window │ ← 0.3 mm
└──────────────────────────────────────────┘
≈ 3.1 mm ── fits the 34 mm budget
```
**Balance / centre-of-mass check:**
- Heaviest single component is the **Li-Po pouch** (~34 g). Placed
centred so the card's CoM stays near geometric centre.
- Display is heavy-ish but mounted at the top half of the front;
battery sits in the lower half of layer 4 to **counterweight**.
- Main PCB components (MCU + modem + secure element) are the densest
silicon area — distribute around the edges of layer 3 rather than
clustering, both for thermal and balance reasons.
- Piezo, the supercap, and the LED drivers go to the *corners* of
layer 3 — naturally balancing each other off-axis.
- Perimeter LED strip + cellular antenna ring the whole card → mass
is symmetric by construction.
**Does it all fit? Yes, with two tight spots:**
- **Qi coil vs. NFC antenna** share X-Y but live on different flex
layers — solved (already shipping pattern). The risk is yield.
- **Front-face real estate**: keypad + display + action row + status
row is exactly the front, with **zero margin** for adding more UI.
Any future feature that needs front-face pixels (touch, fingerprint
reader) means dropping a key or shrinking the display. Treat the
front as **fully booked**.
Anything else added later — fingerprint sensor, second display,
solar trickle cells, larger speaker — has to come out of layer 3
(displace silicon) or grow the Z budget past 4 mm.
### Function ### Function
- **Retrieval surface for E2EE + crypto.** Holds the keys / recovery material - **Retrieval surface for E2EE + crypto.** Holds the keys / recovery material
needed to decrypt member content and authorize crypto operations. Framed needed to decrypt member content and authorize crypto operations. Framed
@ -209,9 +339,24 @@ start, both confirm, no roles.
- SIM/radio provider strategy (MVNO vs. satellite IoT vs. LoRa-style mesh)? - SIM/radio provider strategy (MVNO vs. satellite IoT vs. LoRa-style mesh)?
- Relationship to existing standards (Ledger/Trezor seed compat? FIDO2? - Relationship to existing standards (Ledger/Trezor seed compat? FIDO2?
custom?). custom?).
- Session channel *after* the NFC introduction — BLE for nearby peers, - **Session channel *after* the NFC introduction — this is the single
in-band over the radio mesh for distant peers, or both? (Pairing biggest unresolved hardware decision.** Pairing transport itself is
transport itself is decided: NFC.) decided (NFC). The session that follows could be:
- **BLE** — natural for nearby peers, off-the-shelf chipsets, also
gives find-my-wallet a free local proximity stage. Costs an extra
antenna + radio + paired-phone UX wrinkle. Does *not* work if
the two members are not physically close (e.g. wallet-to-wallet
remote tipping).
- **In-band cellular, platform-relayed.** Both wallets push the
session through the cocotte platform over their own LTE-M
radios. Works at any distance. Costs platform availability for
every transaction and adds latency (sub-second still, but not
instant). Simpler BOM — no BLE radio at all. Find-my-wallet
loses the BLE proximity stage.
- **Custom sub-GHz peer mesh (LoRa-class).** No platform in the
loop, works peer-to-peer at range, but adds another radio +
antenna + cert burden.
Decision blocks BOM (BLE chipset yes/no) and find-my-wallet design.
- **Pogo-pad layout + adapter form factor.** Charging is decided - **Pogo-pad layout + adapter form factor.** Charging is decided
(Qi primary + bundled USB-C→pogo adapter using the same back-pad (Qi primary + bundled USB-C→pogo adapter using the same back-pad
contacts that also serve factory recovery). The contact area is a contacts that also serve factory recovery). The contact area is a
@ -281,14 +426,48 @@ that critique — but worth being explicit about the framing.
fine, the hotel pad doesn't. Source: fine, the hotel pad doesn't. Source:
- https://graniteriverlabs.com/en-us/technical-blog/beyond-qi2-wireless-charging - https://graniteriverlabs.com/en-us/technical-blog/beyond-qi2-wireless-charging
**Power budget — the actual hard problem.** **Power budget — solvable with a 3-tier hybrid stack.**
- A card-thin Li-Po cell is on the order of 100200 mAh. Cellular LTE-M
transmit bursts pull 100 mA+. The wallet must be **mostly asleep**: The actual hard problem isn't bulk energy, it's the **peak-current
- Radio idle in LOCK (beacon-only or fully off). mismatch**: a card-thin Li-Po can hold a few hundred mWh but can't
- Radio wakes only on toggle change, NFC tap, or scheduled push. deliver the 13 A spikes that LTE-M / NB-IoT transmit bursts demand
- Display is e-ink so retention is free. for 12 seconds. The IoT industry already solved this for smart meters
This is a design constraint, not a blocker — IoT cellular trackers and cellular trackers; the same architecture maps directly onto a
achieve months of life at this duty cycle. membership wallet:
1. **Bulk energy — ultra-thin Li-Po pouch.** Mass-produced down to
0.41.0 mm. A 1.52.0 mm card-style cell delivers ~100160 mAh at
3.7 V (Serui, PADRE, Grepow, UFine, Samsung/LG plastic-pouch parts).
Honeywell already ships products with 0.8 mm cells. Chinese supply
chain is mature here.
2. **Pulse buffer — supercapacitor (or Li-SOCl₂ + supercap hybrid).**
Absorbs the 13 A LTE-M transmit burst the Li-Po alone can't
deliver. A 220 F hybrid supercap part delivers ~15 A peak. During
sleep the Li-Po trickle-recharges the cap. This is the *de facto*
architecture for cellular IoT in 2026.
3. **Aspirational always-on trickle — betavoltaic ("diamond")
battery.** Betavolt's Ni-63 / diamond unit is **15 × 15 × 5 mm**
today at 100 µW; a 1 W version is announced. Microwatts are
nowhere near LTE-M's needs **directly**, but the betavoltaic's
role is *trickle-recharging the supercap and Li-Po while the
wallet sits in a drawer*. 50-year half-life on the isotope ≈ a
wallet that doesn't go flat if a member loses it in a couch for a
year. Real status: still pre-mass-market, regulatory questions on
consumer radionuclides — treat as **v2 ambition, not v1 BOM**.
Sources:
- https://www.serui-battery.com/News/xingyezixun/ultra-thin-1-5-2-0mm-3-7v-100mah-card-style-lithium-polymer-batteries.html
- https://www.pdbattery.com/ultra-thin-battery.html
- https://www.grepow.com/blog/ultra-thin-lithium-polymer-battery-for-thinnest-application.html
- https://www.digikey.com/en/articles/use-hybrids-to-bring-the-benefits-of-both-batteries-and-supercapacitors-to-power-iot-designs
- https://hackaday.com/2026/04/28/2026-green-powered-challenge-supercapacitor-enables-high-power-iot/
- https://newatlas.com/energy/betavolt-diamond-nuclear-battery/
- https://www.world-nuclear-news.org/articles/nuclear-battery-chinese-firm-aiming-for-mass-mark
Duty-cycle posture remains required regardless of stack:
- Radio idle in LOCK (beacon-only or fully off).
- Radio wakes only on toggle change, NFC tap, or scheduled push.
- E-ink display retention is free (no draw while showing balance).
**Overall feasibility verdict — buildable with 2026 parts.** **Overall feasibility verdict — buildable with 2026 parts.**
- No single component requires invention. Every piece ships somewhere. - No single component requires invention. Every piece ships somewhere.
@ -301,6 +480,121 @@ that critique — but worth being explicit about the framing.
than greenfield. ODM relationship + custom firmware + cocottetech than greenfield. ODM relationship + custom firmware + cocottetech
brand layer is the shape of the project. brand layer is the shape of the project.
### Find-my-wallet
The cellular radio that exists for transfers doubles as a built-in
**self-locator** — fundamentally better than AirTag / Tile (which rely
on a crowd-sourced mesh of other people's phones). The wallet phones
home on its own.
**The trigger is the *platform*, not the phone.** Member says "find my
wallet" from the cocotte app, web view, or even SMS to the platform.
The platform pushes an **OTA command** to the wallet over LTE-M —
exactly the same downlink path used for any other platform-initiated
message. The wallet doesn't have to be near the member's phone, BLE,
or wifi to receive the find request. Critically: this means **a member
who has lost both their wallet *and* their phone can still find the
wallet** — they log in on a friend's device or call the platform.
Three response stages, escalating with how close the member is:
1. **Global — cellular self-report (LTE-M / NB-IoT).** On receipt of
the platform OTA find command, the wallet immediately wakes the
modem, posts the nearest cell tower IDs + signal strength back to
the platform, and the member sees a city-level pin in the app /
web. Wallets also self-report on a slow background schedule (e.g.
1×/hour while in LOCK) so the *last known location* is always
warm — useful when cellular happens to drop at the moment the
member panics. Power impact is bounded by the duty-cycle posture
already designed in (modem otherwise idle).
2. **Local (~10100 m) — *only if BLE is already on the BOM*.** This
stage is **conditional**, not a given. BLE was originally proposed
as the post-NFC session channel for peer transfers (see the
transfer-session-channel open question). If that decision lands
on BLE, then find-mode gets a free hot/cold proximity stage — the
platform's OTA command flips the wallet into a higher-rate beacon
and a member's phone shows a range bar. **If transfers end up
running over cellular (relayed through the platform) instead of
BLE, this whole stage disappears** and we go directly from stage
1 (city-block cellular pin) to stage 3 (strobe + beep). The
wallet is loud and bright enough on platform command that the
missing middle stage is acceptable — you walk into the apartment
and follow your ears.
3. **In-hand (~cm) — perimeter LED strobe + loud piezo tone.** Same
OTA escalation. The wallet drives the **full perimeter LED strip**
in a high-contrast strobe (reusing the transfer-state hardware —
see the Light language section) and fires a **simple loud tone
generator** — sub-1 mm piezo bender driving a single fixed audible
frequency (~34 kHz, the ear's peak sensitivity). Not a speaker,
not a musical synth, not voice prompts: one tone, intermittent,
loud enough to find the wallet under a couch cushion or inside a
bag. The simplicity is the point — no codec, no DAC, single GPIO
driving the piezo through a step-up, sub-mA at the cap-buffered
peak.
Escalation is **member-controlled from the platform UI**, not
automatic by RSSI — "show me on a map", "make it beep", "make it
beep and flash" are three buttons that send three different OTA
levels. The wallet has no idea how close the member's phone is; it
just does what the platform last told it to do.
**The 30-year-old wallet argument for BLE.**
Re-examining the BLE question on **longevity / degradation** grounds:
imagine the wallet is 30 years old. The piezo bender has died (glue
fatigue, mechanical wear, solder joint failure). The perimeter LED
strip has dimmed past usefulness (phosphor degradation, driver IC
end-of-life). The e-ink display ghosts. Battery is shot — but the
betavoltaic trickle (v2) keeps the radios alive.
In that future, **only the solid-state radios are reliably alive**:
the cellular modem and (if equipped) BLE. The find sequence
degrades as follows:
| Stage | Hardware needed | 30-yr survival |
|---|---|---|
| 1. Cellular pin | LTE-M modem + antenna | ✓ likely OK |
| 2. BLE proximity | BLE chipset + antenna | ✓ likely OK |
| 3a. Strobe | LED strip + drivers + power | ✗ failure-prone |
| 3b. Piezo tone | piezo bender + step-up | ✗ failure-prone |
Without BLE, a 30-year-old wallet with dead transducers gives you a
city-block pin and nothing else — you can't narrow from "in this
house" to "in this drawer". **With BLE, even with all output
hardware dead, the searcher's phone becomes the UI** (sound,
vibration, visual hot/cold meter all run on the phone side). The
wallet just has to emit a silent beacon — which is the same thing a
$5 Tile does, with no transducers at all.
This reframes the BLE BOM question: BLE is justified **not only by
the transfer-session-channel decision, but independently by
**longevity guarantee****. A wallet that's a membership-grade
artefact (potentially handed down, kept for decades) needs at least
two redundant find paths, and the two that age best are both
radios.
Verdict shift: **lean toward BLE on the BOM** regardless of the
transfer-session-channel outcome. Even if transfers run
platform-relayed over cellular, BLE earns its place as the
last-mile finder when local output transducers are end-of-life.
**State + privacy:**
- Find-mode is an **out-of-band command**, not a switch position. The
hardware toggle stays where the member left it (LOCK / SEND / RECV);
find is just an additional behaviour layered on top by the radio
firmware.
- The wallet's location reports are visible **only to the member's
authenticated account** on the platform. The platform never shares
wallet pings with third parties — and per the [[stance]] section,
not with any regulatory or law-enforcement query either.
- A member can **disable cellular self-reporting** in the app (the
wallet then only locates via BLE while the member is within range,
like a dumb Tile). Default is on; member-controlled.
- "Anti-stalker" inversion is a non-problem here: unlike AirTag, this
wallet can't be slipped into someone else's bag to track them — it's
bound to a member account at provisioning and only reports to that
account's owner.
### Prior art — is anyone already doing this? (2026-05-18) ### Prior art — is anyone already doing this? (2026-05-18)
**Short answer:** every *ingredient* ships somewhere; **the exact **Short answer:** every *ingredient* ships somewhere; **the exact

View file

@ -6,6 +6,15 @@ import { EngagementEventEntity } from './engagement-event.entity.js';
import { MailboxEntity } from './mailbox.entity.js'; import { MailboxEntity } from './mailbox.entity.js';
import { OrgEntity } from './org.entity.js'; import { OrgEntity } from './org.entity.js';
import { OrgMemberEntity } from './org-member.entity.js'; import { OrgMemberEntity } from './org-member.entity.js';
import { PeerCollaborationEntity } from './peer-collaboration.entity.js';
import { PeerConnectionEntity } from './peer-connection.entity.js';
import { PeerEndorsementEntity } from './peer-endorsement.entity.js';
import { PeerGroupEntity } from './peer-group.entity.js';
import { PeerGroupMemberEntity } from './peer-group-member.entity.js';
import { PeerGroupMessageEntity } from './peer-group-message.entity.js';
import { PeerMentorshipEntity } from './peer-mentorship.entity.js';
import { PeerMessageEntity } from './peer-message.entity.js';
import { PeerProfileEntity } from './peer-profile.entity.js';
import { PersonaEntity } from './persona.entity.js'; import { PersonaEntity } from './persona.entity.js';
import { UserEntity } from './user.entity.js'; import { UserEntity } from './user.entity.js';
@ -20,6 +29,15 @@ export const entities = [
AgentActionEntity, AgentActionEntity,
EngagementEventEntity, EngagementEventEntity,
MailboxEntity, MailboxEntity,
PeerProfileEntity,
PeerConnectionEntity,
PeerMessageEntity,
PeerGroupEntity,
PeerGroupMemberEntity,
PeerGroupMessageEntity,
PeerEndorsementEntity,
PeerCollaborationEntity,
PeerMentorshipEntity,
] as const; ] as const;
export { AgentActionEntity }; export { AgentActionEntity };
@ -30,6 +48,15 @@ export { EngagementEventEntity };
export { MailboxEntity }; export { MailboxEntity };
export { OrgEntity }; export { OrgEntity };
export { OrgMemberEntity }; export { OrgMemberEntity };
export { PeerCollaborationEntity };
export { PeerConnectionEntity };
export { PeerEndorsementEntity };
export { PeerGroupEntity };
export { PeerGroupMemberEntity };
export { PeerGroupMessageEntity };
export { PeerMentorshipEntity };
export { PeerMessageEntity };
export { PeerProfileEntity };
export { PersonaEntity }; export { PersonaEntity };
export { UserEntity }; export { UserEntity };