Files
platform/src/databases/officer_db/src/index.ts
T
pastilhasandClaude Opus 5 b94a5e90b8 SendOff — a file lands in a real directory in the recipient's own tree
Implements docs/sendoff.md's v2: a folder convention, one endpoint and a push.
No staging area, no transfer table, no ack, nothing to expire. Every account
already has a home, a file browser over it and a Files app on the phone, so a
delivered file is an ordinary file from the moment it arrives.

`Documents/SendOff/<machine>/` for something you sent yourself, and
`Documents/SendOff/<sender>/` for another account's files. The home is the one
that account actually browses — the owner's real login home, a member's managed
home — because the two are different directories and writing to the wrong one
makes a delivery invisible rather than wrong.

CONSENT IS THE RECIPIENT'S, held as a row per pair. Not a role grant: a grant
answers "may this role send files", and the person who should decide whether
somebody may write into your home is you. Absence denies, and a sender who was
not allowed does not see the recipient in their picker at all — the endpoint
answers 404 for "no such account" and for "did not allow you" alike, so it cannot
be used to enumerate accounts. The owner bypasses this as a SENDER only; receiving
still needs their say-so.

The sender's folder name comes from the token and never from the payload, so it
cannot lie about who put a file there. Names are sanitised to one path segment and
filenames to their basename — verified: `../../../../etc` becomes a literal
folder, and a filename of `../../../../tmp/pwned.txt` writes `pwned.txt` inside
the target. Collisions suffix ` (1)`; nothing is ever overwritten, because the
sender cannot see the directory and would have no way to know.

The push carries the folder name and a file count, and the catalogue renders
"3 file(s) from andre" on the device. That relaxes "content does not travel" by a
folder name and an integer — the owner's call, on the grounds that the file never
leaves the tailnet and a notification nobody can act on is not worth sending. A
filename would be content; whose folder it is, is addressing.

`kind: 'app'` rather than `confined` even though it writes into a home: the path
is one fixed shape chosen by the server from the caller's token, so there is
nothing to traverse and nothing to read back. Requiring a Linux user would mean an
account without one could not be sent a file, which is most of the point.

Backend and verified end to end. The web send form is next.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-23 02:32:09 +00:00

60 lines
3.0 KiB
TypeScript

// The package's public surface.
//
// One line per feature, and nothing else. What each feature exports is stated in its own index.ts,
// beside the schema and queries it exports — so adding a query function means editing one file in one
// directory, not that file plus a list three levels up that nobody remembers to update.
//
// This was 297 lines of hand-written named exports until 2026-08-13. Every symbol was listed here, twice
// for most features (values, then types, repeating the path), and `db` and `schema` sat at line 270 with
// three feature blocks appended after them.
//
// The surface is unchanged: the same names are exported, they are just declared next to what they
// describe. 107 files import from 'officerdb' and none of them notice.
//
// ── Core above, plugins below ──
//
// The split mirrors schema.ts, where the plugin TABLES are commented out so db:push does not create
// them. These lines are NOT commented, and the difference is worth stating: commenting them would break
// nothing at runtime — hono.ts no longer mounts a single plugin router, so none of this is ever
// reached — but `bunx tsgo` checks every file under src/ whether it runs or not, and the plugin
// sidecars still import these symbols. So they stay exported until each plugin is extracted, and the
// blank line below is the only thing marking the boundary.
// The connection, and drizzle-kit's view of the schema. See ./schema.ts for the core/plugin split.
export { db, waitForDatabase } from './db';
export * as schema from './schema';
// ── Core ─────────────────────────────────────────────────────────────────────────────────────────
export * from './agent-panels';
export * from './api-keys';
export * from './app-store';
export * from './plugins';
export * from './auth';
export * from './permissions';
export * from './chat-events';
export * from './chat-session-models';
export * from './chat-notification-prefs';
export * from './nudges';
export * from './ai-provider-usage';
export * from './anthropic-accounts';
export * from './theme-favorites';
export { forgetChatSessions, sessionKeyForTranscriptId } from './chat-session-cleanup';
export * from './sendoff';
export * from './command-hq';
export * from './dashboards';
export * from './integrations';
export * from './pipeline-jobs';
export * from './server';
export * from './service-connections';
export * from './user-data';
// ── Plugins — exported only so tsgo stays clean; nothing mounts them ──────────────────────────────
export * from './dav';
export * from './notify';
// `operations` is absent because it no longer exists: it held task_logs, and the Task Logs feature was
// deleted end to end on 2026-08-13 after `task-logger.ts` turned out to have no callers — a full read
// path over a table nothing could write to.