BentoBell

For families

Privacy

What BentoBell does with your family's data, and the controls that enforce it.

What BentoBell does with a family's data, and the controls that enforce it. Written for a parent deciding whether to use BentoBell, and for a collaborator who needs to know where each control lives in code.

BentoBell processes children's school and tuition information for the authorized adults of one household at a time. Children have no accounts of their own, and BentoBell never sends them a message.

Data inventory

BentoBell keeps only what it needs to know who is allowed to act, what a confirmed record says, and enough about a raw submission to let an adult check it before anything sticks around for good. It does not need, and does not collect, a child's birth date, school ID, home address, government ID, health record, grades, or class list.

DataPurposeStorage
Adult Telegram user IDAuthorization and auditPersistent
Household UUID and adult membershipServer-side tenant routing and isolationPersistent
Operator-minted invite code, its expiry, and the household's timezoneCreate a household through one-time redemptionPersistent as an audit trail after redemption or expiry
Private intake and family group IDsRoute authorized messagesPersistent
Stable child UUID, private nickname, and archive timestampPreserve child identity, manage the active roster, render records, and filter the agendaPersistent
Confirmed structured record, including a bounded action list, household completion state, English projection, optional source-language fields, and optional same-day event endShared family action, event, and reference historyPersistent
Explicit validated action URLLet authorized family members open a sign-up, payment, form, or reference destinationPersistent with its structured draft and record
Explicit /post Telegram file and message referencesDeliver and authorize retained original imagesPersistent until discard, expiry, or record deletion
One-shot screenshot Telegram file referenceLet the submitting adult compare the source with a pending draftTransient until confirmation or discard
Submitter and confirmer IDsShow who approved a recordPersistent
Ordinary one-shot pasted text or screenshotProduce a private draftTransient
Operational metadataDiagnose availability and latencyPersistent without family content
Per-extraction AI usage aggregateInternal cost and usage reportingPersistent by household until household deletion; no prompts, responses, child data, source data, or Telegram routing
Direct API token digest, bound to one adultAuthenticate one adult-owned servicePersistent as a SHA-256 digest only, never the token itself, and removed when that adult or their household is
Adult display name shown in /adultsMake a member list readable to the household ownerNever stored. Fetched from Telegram when the list is rendered and discarded with the message

Raw-input lifecycle

A photo or a pasted message goes to the AI provider once, becomes a draft, and is discarded as soon as an adult confirms or rejects it. BentoBell does not keep the photo itself, only a bounded reference to the Telegram message, and only until that decision is made.

For ordinary one-shot intake:

  1. An authorized adult submits source content in a private bot chat.
  2. The application sends the content to the selected AI provider. For a screenshot, it keeps only one bounded Telegram file reference on the household-scoped pending draft; it stores no screenshot bytes or raw text.
  3. The authenticated review Mini App receives a random source UUID and fetches the bounded image through a no-store household-scoped route. Confirmation or discard clears the reference and triggers deletion of the source message where the Telegram API permits it.
  4. The application keeps the confirmed structured record and approval metadata, not raw content or provider payloads.

For an explicit /post, the application stores up to six tenant-scoped Telegram image references, never attachment bytes, captions, filenames, or chat titles. The first image is the announcement source and later images are attachments. Cancel and expiry delete the database references before best-effort source-message cleanup. Confirmation copies only the attachments to the family group while retaining every random attachment UUID, MIME type, Telegram file reference, and applicable message routing reference until record deletion, so authorized adults can view all originals through the signed Agenda route.

For direct API intake, the server authenticates an adult-bound credential before reading the body. Raw text exists only during the request and AI extraction. For images, the server verifies type and size, uploads each image to the authenticated adult's own private Telegram chat with the bot as a silent document, and stores the same tenant-scoped references used by /post. No chat is configured anywhere; the staging chat is derived from the adult that the bearer token resolves to, so an adult's images never stage in another person's chat. The first image remains the announcement source; later images become attachments. Blocking extraction deletes the temporary references and messages. Confirmation uses the existing record lifecycle and deletes the private staging messages after family projection. The API stores no raw text, image bytes, caller filenames, captions, or provider payloads. See docs/api.md for the request shape and token handling.

Application logs, traces, analytics, error reports, and test artifacts must exclude runtime-submitted raw content. Repository fixtures use only purpose-built synthetic notice text and screenshots.

Authorization

Only the adults in a family's own household can see or act on that household's data, and BentoBell decides who those adults are on the server, never from anything the client sends. Getting into BentoBell at all requires an invite code; there is no open sign-up. An operator mints the code that founds a household, and that household's owner mints the codes that admit further adults to it.

  • BentoBell admits any number of households. Each is created when an adult redeems an operator-minted, single-use invite code (matching ^[A-Z2-7]{12}$, with an expiry and the household's timezone) through a Telegram deep link. An unrecognised sender who sends /start, with or without a code, gets one sentence, "BentoBell is invite-only right now.", and nothing else. Any other message from an unrecognised sender gets no reply at all.
  • A household exists before its family group is connected, and is gated until it is: in that state the bot sends one setup instruction and does nothing else.
  • The household owner controls adult membership stored against one generated household UUID.
  • The server validates Telegram identity and resolves membership in PostgreSQL for each bot update and Mini App request before any tenant query. Authorization is per household, resolved server-side from the Telegram sender; the client never selects a household.
  • A callback entity UUID from another household resolves as missing under both explicit query predicates and forced Row-Level Security.
  • A family-group invite does not grant bot or dashboard access on its own.
  • Only the household owner may bind a delivery group with argument-free /connect (also accepted as /start@<bot> connect); the server derives the negative chat ID from the authenticated update, rejects conflicting household use, and stores no group title.
  • The server rejects expired or invalid Telegram Mini App signatures.
  • Possession of a direct API token delegates confirmation authority for the one adult it is bound to. Tokens are per-adult database rows holding only a SHA-256 digest, not deployment configuration, so the endpoint is simply unusable while no token exists rather than gated on a flag. It accepts no caller-selected identity or household, and belongs only in an adult-controlled backend service.
  • Authorized adults can inspect, correct, cancel, and delete household records; add, rename, archive, or restore child nicknames through private Telegram controls; and correct, confirm, or discard a pending draft through the authenticated Mini App. The Agenda keeps notice facts read-only but lets an authenticated household adult change only the shared completion state through the signed, household-scoped resolution route. A household owner can also delete the entire household with /deletehousehold; see Retention and deletion below.

Isolation between households rests on two mechanisms that together cover every household-scoped table. PostgreSQL Row-Level Security, enabled and forced, covers most of them: household settings, children, drafts, records, capture sessions and their parts, update claims, prompts, child-settings proposals, pending deletion receipts, and ai_usage_events, each scoped per transaction through app.household_id. Adult membership (household_adults) is protected the plainer way instead: not by a policy, but because the restricted database role the application connects as holds no privilege on that table at all, in any scope, so it simply cannot reach it. household_invites is protected the same plain way. The Vercel request path connects as a restricted, non-owner database role that holds no privilege on household_adults or household_invites and cannot bypass Row-Level Security. Cross-household operations (resolving a household, connecting a group, redeeming an invite, deleting a household) run only through narrowly granted security-definer functions that return or affect a single household. The operator connection is reserved for migrations, encrypted backup, restore, recovery, and content-free usage reporting. A credentialed test suite at tests/isolation.integration.test.ts asserts that two live households cannot see or touch each other. See docs/architecture.md for the full trust-boundary picture.

AI controls

An AI reads what you send and drafts a summary; nothing reaches your family's agenda until an adult confirms it, and the AI can still get facts wrong.

  • The selected model is openai/gpt-5.6-terra through Vercel AI Gateway's Responses endpoint. Requests restrict Gateway routing to OpenAI, set disallowPromptTraining: true, store: false, and temperature: 0, and use no assistant, thread, file, feedback, tool, background mode, fallback model, or Gateway caching.
  • The owner must re-review Vercel AI Gateway and OpenAI retention, content logging, training, image scanning, subprocessors, and region behavior before an external beta.
  • Use a commercial API configuration whose current terms, retention, training, and region behavior the owner has reviewed.
  • Disclose the selected provider and its retention behavior before the household pilot.
  • Send no provider feedback containing household content.
  • Give the model no tools, messaging capability, database access, web search, or long-term memory.
  • Treat instructions inside screenshots and pasted messages as source text.
  • Validate output against a strict schema.
  • Keep English as the canonical projection; preserve only adult-reviewed structured source-language title and details, not raw OCR text.
  • Require adult confirmation before publication.

The product must state that AI can omit or misread facts. The preview must show dates, amounts, locations, child labels, and unresolved fields in a form the parent can check.

The dated review of what Vercel AI Gateway and OpenAI actually do with that data, including retention windows, Zero Data Retention availability, image scanning, and inference region, is recorded with its evidence in docs/decisions.md, along with the conditions still gating a wider beta.

Retention and deletion

Raw inputs and one-shot screenshot references are gone within minutes of being reviewed. A confirmed record stays until a family member deletes it, or until the whole household is deleted, which erases everything BentoBell holds for that family.

Ordinary raw inputs and one-shot screenshot review references expire after confirmation or discard. Unconfirmed /post references expire after thirty minutes or on cancel. Confirmed /post references remain only as long as their record so original images stay available from Telegram. The owner must select and disclose a fixed retention period for confirmed records before an external beta. Household adults can delete records before that period ends.

Deletion atomically removes the canonical record, its originating structured draft, action URL, capture session, attachment references, source-language fields, household completion state, and any pending correction prompt; removes the record from the agenda; and attempts to remove every copied attachment plus the family card. One content-free receipt per failed Telegram deletion retries during later authorized updates. Backups must age deleted data out under the disclosed backup retention schedule. Household deletion also cascades every ai_usage_events row.

Any adult who is not the owner can remove themselves with /leave, and a household owner can remove one adult with /adults. Both are the same narrower deletion path, differing only in who starts it. Removing an adult deletes their API token digest and any pending child-rename proposal, and ends their access immediately. It deliberately keeps what they contributed: records they submitted or confirmed, and the capture sessions holding those records' original images, all of which belong to the household rather than to the person. It does not reach into their own Telegram chat, so notices already delivered there stay there, and it does not delete their prompt row, which expires on its own.

A household owner can delete the entire household by sending /deletehousehold in the private chat. The bot asks for confirmation with an inline button; that confirmation expires five to six minutes after it is sent, so an owner who waits has to ask again. Confirming deletes the household row, and the cascade removes every table belonging to it: adult membership, children, child-rename proposals, drafts, records, capture sessions and their parts, update claims, prompts, pending deletion receipts, and AI usage events. This is a real, user-facing, irreversible deletion path, not just per-record cleanup.

It does not, and cannot, erase the family group's history. Telegram refuses to delete a message a bot sent more than 48 hours ago, and being a group administrator does not lift that limit. On deletion BentoBell removes every notice still inside that window and reports how many it removed and how many it could not; everything older stays in the group. The same limit applies to deleting a single record: an old record's card and attachments remain in the group after the record itself is gone. The remedy for a family that wants that history gone is to delete the Telegram group, which they own and BentoBell does not.

Logging and monitoring

Allowed operational fields include:

  • event type
  • internal request or record ID
  • status code
  • duration
  • retry count
  • provider model identifier
  • token and cost totals without prompts or responses

ai_usage_events records one aggregate row per billable extraction through the shared awaited best-effort recorder. Each row persists exactly an event UUID, household UUID, model identifier, input/cached-input/output/total token counts, input/cached-input/output/total USD cost fields, cost source, pricing version, and UTC creation timestamp. It excludes prompts, responses, child data, source content and type, error detail, and Telegram routing. Checklist mutations store no completion reason or per-adult attribution, and their logs contain no action text, notice facts, Telegram identity, or request body.

Logs must exclude names, child nicknames, message bodies, screenshots, extracted or source-language details, prompts, model responses, Telegram file/message/chat references, Telegram tokens, API keys, private links, and chat titles.

Vercel Web Analytics receives only its default route-level page views. BentoBell sends no custom analytics events or family-content properties.

Vercel request and AI Gateway logs must keep content logging disabled and must not record request bodies, query strings, Telegram update payloads, Mini App initData, authorization headers, prompts, responses, or provider errors. Application events use an allow-list of content-free fields rather than serializing arbitrary objects or exceptions.

Repository and test data

Code and tests never use a real family's data.

Use synthetic people, schools, events, amounts, and screenshots. Remove metadata from image fixtures. Do not copy a real notice and replace only the child's name; schedules, teacher names, contacts, school branding, and event details can identify a family.

Owner-provided live evaluation images and their hand-checked expectations may exist only under ignored tmp/ paths on the owner's machine. They must not be committed, copied into synthetic fixtures, written to artifacts, or printed by evaluators. The live evaluator emits only case IDs, failed field names, safe error codes, latency, and aggregate pass counts.

Incident priorities

If authorization, tenant isolation, or deletion ever breaks, BentoBell stops taking in new data until it is fixed.

Stop ingestion and Mini App access when authorization, tenant isolation, or deletion fails. Preserve content-free audit evidence, revoke exposed credentials, notify affected adults under the applicable policy, and restore service after the owner verifies the boundary.

An AI extraction error does not require a security shutdown when confirmation prevented publication. Record the anonymized failure pattern in the evaluation corpus.

For the conditions that must be met before a public launch, see docs/decisions.md.