platepilot

Threat model

Scope

This model covers the M0 local scaffold and the specified future PlatePilot beta: an adult-only mobile experience, REST API, asynchronous meal analysis, two-adult households, internal support surface, and third-party providers.

M0 has no production deployment, real adapters, customer dataset, or complete identity/authorization flow. Controls marked required are design requirements, not claims about current behavior.

Safety and privacy assumptions

Age assurance, jurisdictional privacy classification, consent wording, and support verification need legal/privacy review before beta.

Assets

Asset Sensitivity Primary concern
Credentials, sessions, recovery factors Restricted Account takeover
Meal photos and object references Restricted Intimate-context disclosure, location metadata, unauthorized reuse
Diary, water, plan, and nutrition records Confidential Profiling, embarrassment, inference of health or routine
Adult identity and household graph Confidential Stalking, coercion, unauthorized sharing
Subscription and entitlement state Confidential Fraud and improper denial of access
Body facts and goals (sex, height, weight, activity, targets) Restricted Health inference, body-image harm if shown carelessly; never shared with a household peer
Exports and deletion evidence Restricted Bulk disclosure or incomplete erasure
Provider credentials and webhook secrets Restricted Vendor impersonation and data exfiltration
Audit events and operational telemetry Confidential Exposure through logs or loss of accountability
Contracts, code, and deployment artifacts Internal Supply-chain compromise and tampering

Actors

Trust zones

flowchart LR
    Device["Adult device<br/>untrusted client"]
    Edge["Future public ingress"]
    App["PlatePilot service zone"]
    Data["Restricted data zone<br/>database and objects"]
    Ops["Operator/support zone"]
    Vendor["External provider zones"]

    Device --> Edge --> App --> Data
    Ops --> App
    App --> Vendor

For M0, the equivalent zones are the developer host, loopback-bound Docker services, application processes, repository, and package registries. Localhost is not a security boundary against other processes on the host.

Abuse cases and controls

Abuse case Impact M0 control Required before beta
Guess or steal an adult’s session Full account exposure No production identity integration Strong managed identity, secure token storage, rotation/revocation, rate limits, recovery safeguards
Change an ID to read another adult’s record Cross-account disclosure Every repository query filters by the caller’s own adult profile; the only cross-adult reads are shared plans (mutual opt-in, read-only) and shared daily totals (owner opt-in); ownership is covered by unit and integration tests Production identity and an exhaustive authorization test matrix
Coerce or trick a second adult into sharing Privacy and safety harm Single-use 7-day invitation codes that carry no data; independent acceptance; two per-adult sharing switches default off; plans shared only when both opt in; leaving is unilateral and clears sharing; nothing is ever copied between adults (ADR-0008) Coercion and post-separation research (TM-04), safety review of copy, rate limits on code guessing
Use a household role to export/delete the other adult Bulk loss/disclosure Export and deletion act only on the caller’s own profile; export contains only the fact sharedWithAnotherAdult; deletion requires X-Confirm-Deletion equal to the caller’s own ID, removes retained photos before rows, and leaves the other adult’s household and rows untouched; both audited Stronger re-verification, cooling-off policy, vendor deletion evidence, backup restore suppression
Upload a polyglot, oversized, corrupt, or malicious file Availability or parser compromise M1 checksum binding, version capture, Sharp decode, and byte/dimension/pixel limits Production malware strategy, isolated processing, and timeouts remain pending
Preserve EXIF location or faces beyond need Context disclosure Raw uploads are quarantined, auto-oriented, re-encoded without metadata, and exact-version deleted before provider processing; only sanitized bytes can reach processing or optional retention Production face-detection validation and vendor handling review remain pending
Replay an upload/job/webhook Duplicate charges or records Async contracts and synthetic mocks Idempotency keys, signed webhooks, freshness checks, deduplication, bounded retries and DLQ
Tamper with an AI estimate Misleading wellness information Deterministic mock only Schema/range validation, provenance, provisional label, review/edit/manual path
Generated meal plan includes an allergen or implies a medical target Physical harm or medical framing Allergens limited to the 14 EU vocabulary; recipe ingredients tagged; generator filters and the API re-checks every meal; each item exposes its allergens and copy says to check labels; focus replaces numeric goals; nutrition labelled as a synthetic per-serving estimate (ADR-0010) Allergen data quality review for any real provider, dietitian/legal review of copy, user testing of the "check labels" wording
Location used for planning reveals where an adult lives Context disclosure Only country plus city or postal code is accepted and stored; a street address is rejected by schema; location is used for product names, sections, and units only Review whether city/postal code is needed at all once a real grocery-data provider is chosen
Exhaust analysis quota or queue Cost and availability impact Per-adult monthly photo-analysis allowance enforced server-side before a new upload is authorized (403 analysis_allowance_exhausted); usage counted from analysis jobs; manual entry unaffected (ADR-0009) Household-level limits, rate limiting, backpressure, budget alarms, abuse review
Substitute mocks or debug mode in production False output or weakened controls No deployment; strict configuration foundation Environment allowlist, startup failure, deployment policy and tests
Inject sensitive values into logs/errors Long-lived disclosure Typed errors and synthetic-data rule; M1 audit events are a strict allowlist (actor, action, public resource ID, request ID, outcome, error code) that rejects unknown fields and free text Structured redaction, payload exclusion, access controls, retention enforcement and tests
Abuse internal support access Broad data disclosure Admin has no customer-data access; every sensitive mutation (onboarding, diary writes, photo intake and worker steps, plan changes and confirmations, household invitations/sharing/leave, subscription changes, alert preferences, export, deletion) is recorded to an in-process audit sink with public IDs only Least-privilege roles, reason codes, approval for high-risk actions, immutable audit, periodic review
Compromise a dependency/build Code execution or secret theft Lockfile and validation Dependency review, provenance/SBOM policy, protected CI, artifact signing and scanning
Lose a device with offline drafts Diary/photo disclosure M1 SQLCipher command/photo-job stores, strict payload/UI schemas, per-adult SecureStore preferences, dedicated temp-media sweep, and sign-out destruction Device/native security review, screen/privacy controls, and remote-session revocation
Infer that a child uses the product Safety/legal impact Adult-only policy Age gate, no dependent/minor fields, support escalation, deletion path

Privacy constraints

  1. Collect only data needed for the adult-selected feature.
  2. Raw uploads land under quarantine/, are decoded, auto-oriented, and re-encoded as JPEG/PNG without EXIF (including GPS). The API stores the sanitized checksum and immutable processing/ version, then deletes the exact raw version. One-day lifecycle rules cover both prefixes as a crash fallback. Providers and optional 365-day retention receive only sanitized bytes.
  3. Do not use photos or wellness records for model training or unrelated advertising without a separately reviewed, explicit decision.
  4. Send vendors the minimum content and pseudonymous identifier needed for the operation.
  5. Do not include photo bytes, signed object URLs, tokens, full provider responses, or diary text in routine logs.
  6. Automated estimates need provenance, uncertainty-friendly copy, review, edit, and manual fallback.
  7. Exports and deletion are per verified adult; household membership does not grant blanket access. The M3 export returns only the adult’s own rows plus the boolean sharedWithAnotherAdult; deletion cascades only the adult’s own rows, drops an empty household, and leaves a receipt holding a one-way subject hash.
  8. Backup expiry, provider deletion, device caches, derived records, and audit exceptions must be disclosed. M1 audit events carry only the adult public ID (or a one-way subject hash before a profile exists), the action, the public resource ID, the request ID, the outcome, and a stable error code; tokens, URLs, food text, nutrition values, birth data, and email cannot pass the schema. Events that fail validation are dropped and counted as an operational signal rather than written.
  9. No child/minor profiles, family-dependent flows, or pediatric content.
  10. Local photo recovery stores only an adult-scoped provider job handle and safe state metadata, never photo bytes or picker/library URIs.
  11. M1 authentication failure does not promise token renewal. The only destructive recovery action states that pending operations and private local data will be discarded, requires confirmation, and signs out normally.
  12. Household sharing is opt-in per adult, mutual for plans, read-only, and limited to plan items and eight daily totals; invitations carry no diary data (ADR-0008).
  13. Alerts are reminders for planned meals and water plus problem notices only; the API never emits threshold or goal warnings, and nothing about alerts is stored server-side.
  14. Generated meal plans use a focus, never calorie or macro targets; allergens are hard exclusions; age enters only as a coarse band from the optional birth year; location is country plus city or postal code (ADR-0010).

Vendor boundaries

No real vendors are connected in M0. LogMeal, Spoonacular, RevenueCat, Cognito, notification, and health adapters are intentionally absent.

Before enabling any provider:

Provider contracts reduce coupling; they do not transfer PlatePilot’s privacy responsibility.

Open risks

ID Risk State / next evidence
TM-01 No production identity or object authorization Open; threat review and authorization test matrix before beta
TM-02 End-to-end deletion propagation to future real providers is not implemented Open; local source and retained-object deletion are enforced, but vendor deletion evidence is still required
TM-03 Native encrypted recovery behavior differs across supported devices Mitigated in M1 with SQLCipher command/photo-job stores, strict fail-closed schemas, ACK/conflict tests, and destructive sign-out; simulator/device validation remains required
TM-04 Household coercion and shared-record semantics need research Partially mitigated: ADR-0008 fixes the shared-record model (opt-in, mutual, read-only plans; totals-only summaries; unilateral leave) and the API enforces it with tests. Still open: adult safety review, coercion and post-separation usability evidence, rate limits on invitation-code guessing
TM-05 Vendor contracts and data-use terms are unreviewed Open; security/privacy assessment for each adapter
TM-06 File-processing and content-safety pipeline is undefined Open; upload design, limits, isolation, and malformed-file tests
TM-07 Confirmed performance targets lack measurement evidence Open; test cached dashboard under 1 second, normal API p95 under 500 ms, and analysis p90 under 15 seconds
TM-08 Export/deletion workflow and legal response periods are unset Partially mitigated: per-adult JSON export and verified deletion (confirmation header, retention-bucket purge first, cascading transaction, receipt) are implemented and exercised locally. Still open: approved legal policy, re-verification method, cooling-off, vendor evidence, backup restore suppression
TM-09 Cloud logging, audit immutability, and support roles are not built Partially mitigated in M1: sensitive mutations emit schema-validated, privacy-safe audit events (succeeded, rejected, and failed outcomes) plus api.request.duration and meal_analysis.completion.duration metrics through in-process sinks only (stdout JSON locally, memory in tests); no cloud delivery, durability, or immutability yet. Still open: durable immutable storage, retention, alerting, support roles, and access review
TM-10 Adult-age assurance may be bypassed Accepted residual risk for M0; method and escalation pending beta review
TM-11 Mock billing allowance is per adult and may diverge from commercial policy Accepted for local development (ADR-0009): configurable per-adult monthly allowance, no charges, lazy trial start. Needs commercial/legal decision on household versus per-adult allowances and explicit trial consent before beta
TM-12 Generated plans depend on ingredient allergen data that a mock cannot guarantee for real recipes Accepted for local development (ADR-0010): synthetic library with hand-tagged allergens, double filtering, per-item allergen exposure, and label-check copy. Before any real provider: allergen data quality assessment, dietitian and legal review, and a kill switch to manual planning

Revisit this model for every new data category, provider, household-sharing rule, offline store, support permission, or deployment topology change.