feat(app): mime-agnostic share-sheet ingest + vault-pit storage#1
Draft
Camaraarthur wants to merge 2 commits into
Draft
feat(app): mime-agnostic share-sheet ingest + vault-pit storage#1Camaraarthur wants to merge 2 commits into
Camaraarthur wants to merge 2 commits into
Conversation
First-time tracking of ~/daemon/app/ — the v0.1 Android orb architecture
that has lived as untracked working-tree files until now.
- SQLCipher-encrypted vault (vault/) + biometric-gated Keystore master key
- LlmProvider abstraction (llm/) with Echo / Anthropic / Mistral /
OpenRouter / Gemini Nano implementations
- PII regex strip (privacy/) + egress audit log (net/)
- Share-with-daemon intent parsing (share/) — currently metadata-only
- ScreenshotWatcher OCR ingest (ingest/) gated to Pictures/Screenshots/
- Compose UI (ui/): chat shell, settings, egress audit, biometric lock
Build artifacts (build/, .gradle/, *.apk), local.properties, and the
parked aquarium/ TripoSR pipeline are excluded.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Before this change the share-sheet handler in app/ parsed inbound
URIs but dropped the bytes on the floor — ChatScreen rendered a
metadata-only "(file-bytes import lands in v0.2)" stub. This wires
the bytes through end to end.
Manifest
- Catch-all */* intent filters for ACTION_SEND + SEND_MULTIPLE
with android:order=999 so daemon ranks high in the share sheet,
no mime allow-list.
- <share-target> declared in res/xml/shortcuts.xml as a Sharing
Shortcuts hint so Android can surface daemon in the top
direct-share row.
Vault (schema v1 → v2)
- files(id, sha256 UNIQUE, name, mime, size_bytes, blob_path,
imported_at) — L0 raw bytes index, content-addressed.
- message_files(msg_id, file_id, ord) — junction so messages can
reference one or more imported files.
- derivations(id, file_id, kind, model, text, blob, meta,
created_at) — L1/L2 slot for future transcripts, OCR,
embeddings, summaries, persona excerpts. Empty in this PR.
- DerivationKind conventions exposed for future pipelines.
- internal fileBlobKey() = HMAC-SHA-256(passphrase,
"daemon-file-blob-v1") so file encryption is domain-separated
from SQLCipher's use of the same passphrase.
FileStore.kt (new)
- Streams URI bytes via ContentResolver → AES-256-GCM encrypts
with a fresh 12-byte IV → writes to
filesDir/blobs/<sha256>.enc.
- Plaintext sha256 is computed in-stream via DigestInputStream
for content-addressed dedup (already-present sha → row is
reused, new blob discarded).
- open(FileRow) returns a CipherInputStream over decrypted
plaintext — entry point for future transcription / embedding
pipelines.
- Mime-agnostic: anything that ContentResolver can openInputStream
is ingestible. No size limit at the store level.
ChatScreen
- SharedPayload.Files now fans through ingestFiles() which:
- calls FileStore.import for each item (in IO),
- renders a single chat summary
("📎 name · mime · size · ✓ in vault"),
- persists the summary as a SYSTEM message and links each
imported file to it via attachFileToMessage().
- SharedPayload.Files ≥ 200 MB total triggers a confirm dialog
("import N GB into daemon?") — per Arthur's "don't fail, ask".
- Vault-locked path emits a useful "locked — couldn't store"
note instead of dropping silently.
End-to-end effect: any file (any mime) shared into daemon lands as
encrypted, dedup'd, indexed bytes in the vault, ready for future
RAG / transcription / persona work to populate the derivations
table on top.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Turns the daemon Android app into a content-addressed data pit: any file shared into daemon from any other app, any mime type, gets streamed → AES-256-GCM encrypted → dedup'd → indexed in the SQLCipher vault.
Before: share-sheet handler parsed inbound URIs but dropped the bytes (
ChatScreen.kt:83-87literally rendered(file-bytes import lands in v0.2)).After: bytes go all the way to disk, the vault knows about them, and the schema has the L1/L2 slots ready for future transcription / embedding / persona work to populate.
Two commits, separately reviewable
e9c21bf—app/v0.1: import Android orb baseline into git— first-time tracking of~/daemon/app/(the v0.1 orb architecture perapp/SPEC.md). It had lived as untracked working-tree files only; this commit imports it as-is. Build artifacts (build/,.gradle/,*.apk),local.properties, and the parkedaquarium/TripoSR pipeline are excluded.ba2b847—feat(app): mime-agnostic share-sheet ingest + vault-pit storage— the actual feature work, scoped to:AndroidManifest.xml— collapse 4 mime-specific filters to one catch-all*/*per action withandroid:order=999; add<meta-data>pointing tores/xml/shortcuts.xml.res/xml/shortcuts.xml(new) —<share-target>for direct-share hinting.vault/Vault.kt— schema v1→v2 migration addingfiles,message_files,derivationstables + an internalfileBlobKey()derived from the SQLCipher passphrase via HMAC-SHA-256 with"daemon-file-blob-v1"for domain separation.vault/FileStore.kt(new) — URI → encrypted sidecar atfilesDir/blobs/<sha256>.enc. Content-addressed dedup.open(FileRow)returns aCipherInputStreamfor future readers.ui/ChatScreen.kt— replaces the metadata-only stub with a real ingest pipeline. Renders📎 name · mime · size · ✓ in vaultand persistsmessage_fileslinkage.What this PR does NOT do
INSERT INTO derivations)ScreenshotWatcherOCR output intoderivations(legacy SYSTEM-message path keeps working untouched)Test plan
./gradlew assembleDebug— ✓ passing (15s in the worktree)..m4afrom Pocket Casts / Files / etc. — verify daemon appears high in share sheet, chip renders with✓ in vault.adb shell run-as dev.daemon.app ls -la files/blobs/— confirm encrypted sidecar exists.✓ already in vault.Notes for review
app/SPEC.md:51says data ingestion is v0.3. This PR moves it forward — vault-as-pit is now treated as core, per chat alignment with @Camaraarthur. SPEC.md edit deliberately not included; vision specs are human-edited.~/daemon/CLAUDE.mdrules document the v0.1app/architecture; that file is not ondevelopand is not added here either — would be a separate doc PR.🤖 Generated with Claude Code