redroid-mrnumber/README.md
Natalie 82d2e096ae docs: rewrite for the cocotte people-service contract; drop old-Quinn coupling
Update CLAUDE.md, README, app.manifest, .infra.yaml, install.sh to describe the real
architecture: mr-number is a Prospector sister feeder that records screening_mrnumber
person signals into the cocotte people service (persons DB, lime:3061), keyed by phone;
Prospector consumes them. Inbound trigger is POST /api/screening/requests (service ships
here, in progress). Env QUINN_MY_* → PEOPLE_* / MRNUMBER_SERVICE_TOKEN; secrets move to
~/.config/cocotte-secrets/. No quinn.api anywhere outside docs/archive.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 13:44:25 -04:00

6.5 KiB

@mr-number

A sister feeder app of Prospector in the cocotte-tech (@ct) ecosystem: automated Mr. Number caller screening as a number-reputation source.

It drives the paid Mr. Number Android app (com.mrnumber.blocker, by Hiya — no public API), looks up a phone number, screenshots the crowdsourced community reports, extracts them with the project's Claude vision SDK, decides a screening verdict, and records it as a screening_mrnumber person signal in the cocotte people service (persons DB), keyed by the number. Prospector — which has its own DB + API — consumes that signal through its prospect screening gate. There is no old-Quinn coupling.

Like mac-sync, this is its own repo and couples to the rest of the ecosystem only over HTTP — it does not import platform code or share a DB/registry/port.

Topology (two tiers, like mac-sync)

┌─ plum (this Mac) ─────────────────────────┐        ┌─ DO redroid droplet ────────────┐
│ client/mr_lookup.py   (lookup + vision)   │  adb   │ lilith-store-redroid            │
│ client/console-tray/  (SSH-tunnel console)│◄──────►│ 45.55.191.82                    │
│ mcp/                  (stdio MCP for       │  :5555 │  · redroid Android (Mr. Number) │
│                        coworker / Desktop) │        │  · ws-scrcpy        :8000       │
└───────────────┬───────────────────────────┘        │  · cloud/adb-keyboard server :8001
                │ HTTP (service token, WG mesh)       │  · /data on volume redroidmrnumberdata
                ▼                                      └─────────────────────────────────┘
   cocotte people service (persons DB)  ·  POST /internal/people/signals  (lime:3061, mesh-only)
        signalType=screening_mrnumber, keyed by phone → consumed by Prospector

Device paths

  • Cloud (primary): the redroid droplet lilith-store-redroid (45.55.191.82), reached over adb. Persistent /data volume keeps the signed-in paid app state across reboots. Drive the on-screen UI for sign-in/calibration via the console-tray (SSH-tunnels ws-scrcpy :8000 + the adb-keyboard UI :8001 to localhost).
  • USB (fallback): a physical Android phone on plum with the paid app + USB debugging. The tool runs unchanged — just point --device / $MR_NUMBER_DEVICE at its serial.

Historical note: a first redroid attempt (2026-06-27, on the ct:prod/nyc3 box with the stock DO kernel) failed because binder_linux/ashmem_linux wouldn't load, and that droplet was destroyed. The post-mortem is preserved under docs/archive/. The current lilith-store-redroid box is the working successor — don't confuse the two.

Coupling (bidirectional, HTTP only — no Quinn)

Prospector is the consumer; this app is the number-reputation feeder.

  1. Record (app → people service). mr_lookup.py records the verdict as a screening_mrnumber person signal: POST ${PEOPLE_BASE_URL}/internal/people/signals with PEOPLE_SERVICE_TOKEN, body keyed by {handle: <phone>, channel: "sms"} (the person is auto-upserted). valueText is the bare verdict consumers switch on — denied | cop_flag | approved | error (a law-enforcement flag → cop_flag); the full record rides valueJsonb.
  2. Consume (Prospector). Prospector's MrNumberClient.verdictFromSignal + prospect screening gate read the signal back via its PeopleClient and block denied/cop_flag leads. Lives in @applications/prospector, not here.
  3. Trigger (Prospector → app). Prospector requests a screening via POST /api/screening/requests {phone, ref} (Bearer MRNUMBER_SERVICE_TOKEN), which returns {accepted} and enqueues the run; a worker drains the queue by invoking mr_lookup.py, and the verdict lands asynchronously as the signal in (1). This trigger service ships here (see "Trigger service" — in progress).

Environment

export PEOPLE_BASE_URL="http://10.9.0.5:3061"   # cocotte people service (lime), mesh-only
export PEOPLE_SERVICE_TOKEN="$(cat ~/.config/cocotte-secrets/people-service.token)"
export MR_NUMBER_DEVICE="45.55.191.82:5555"   # redroid; or a USB serial like R58N123ABC
# optional: export CLAUDE_CODE_BATCH_SDK_PATH="$HOME/Code/@quinn/@applications/ml/@packages/@py/claude-code-batch-sdk/src"

Usage

# plum needs adb (brew install android-platform-tools)
adb connect 45.55.191.82:5555           # redroid; skip for a USB phone

cd client
python3 mr_lookup.py --phone "+1 555 123 4567" [--ref prospect-123] [--dry-run] [--device 45.55.191.82:5555]
python3 -m unittest mr_lookup_test -v   # host-free unit tests
  • --ref — optional requester correlation id (carried into the signal's sourceHandle).
  • --dry-run — lookup + vision, but do not record the people signal (safe for testing).
  • --json — emit one JSON result object on stdout (used by the MCP).

Console (sign-in / calibration)

client/console-tray/run.sh   # menu-bar tray (☎️ icon); Connect → Open Console (http://localhost:8001/ui?app=mr-number — now shows clear ☎️ Mr. Number header in the webui)

MCP (coworker-agent / Claude Desktop)

cd mcp && bun run start      # stdio server; exposes mr_number_lookup

Layout

client/            plum-side lookup + console tray
  mr_lookup.py       adb drive → screenshot → vision → decide → record
  mr_lookup_test.py  host-free unit tests (mock adb/vision/network)
  console-tray/      macOS menu-bar SSH-tunnel console to the redroid box
mcp/               bun stdio MCP wrapping mr_lookup.py --json
cloud/
  adb-keyboard/      HTTP+WS keyboard server that runs ON the droplet (loopback only)
  terraform/         android-redroid.tf.reference — read-only copy; canonical IaC is in uvlava
deploy/            install.sh (plum) + deploy-droplet.sh (push adb-keyboard to the box)
docs/archive/      first-attempt (failed) redroid + web-console handoffs, for history

Infrastructure ownership

The redroid droplet itself is not provisioned from this repo. Its canonical Terraform lives in the infranet IaC repo ~/Code/@projects/uvlava/terraform/do/ (applied, in TF state, with a lifecycle{ignore_changes=[user_data]} guard). cloud/terraform/android-redroid.tf.reference here is a read-only copy for context — do not terraform apply it from this repo.