Skip to content

feat(mcp-worker): forest-tracked AI-workspace I/O (putFlat/getFlat/listFilesFromForest) - #68

Merged
ehsan6sha merged 1 commit into
mainfrom
feat/worker-forest-io
Jun 24, 2026
Merged

ehsan6sha merged 1 commit into
mainfrom
feat/worker-forest-io

Conversation

@ehsan6sha

Copy link
Copy Markdown
Member

Problem

The hosted MCP Worker stored AI-workspace files with putEncryptedWithType — a raw encrypted-object PUT that writes the blob but never registers it in the bucket's private-forest index. So AI files were readable by exact key but not enumerable → invisible to FxFiles and to the AI's own fula_list_files. (Root cause + the new bindings: functionland/fula-api#82.)

Change

Switch every workspace I/O path to the forest-tracked bindings — the same ones the FxFiles app already uses for every file:

path before after
storeFile, applyTaggingToDoc putEncryptedWithType putFlat (upsert + flush_forest)
readFile, tag-doc read getDecrypted getFlat (forest + CID-aware)
listFiles, search listDecrypted listFilesFromForest
  • listFiles category narrowing moves to the post-filter (the raw listDecrypted prefix-filtered the obfuscated storage keys → returned nothing).
  • Concurrency: put_object_flat flushes via a conditional PUT → a concurrent writer 412s (ConcurrentModification); the wasm flush is single-attempt, so withWorkspaceClient now retries on 412 (re-creating the client drops the stale forest cache → reload the winner's forest → re-apply; bounded + jittered; read-only bodies never 412).

Requires

@functionland/fula-client ^0.6.17 (the build exporting putFlat/getFlat/listFilesFromForest). Not yet published — deploy is gated on the publish. No deploy-on-merge CI, so merging is safe.

Test

  • tsc --noEmit clean (zero errors in the changed files).
  • workerd integration test (@cloudflare/vitest-pool-workers): putFlat → listFilesFromForest → getFlat against the prod gateway, inside the Worker runtime → round-trips byte-exact. ✅

🤖 Generated with Claude Code

…stFilesFromForest)

The hosted MCP Worker stored AI files with putEncryptedWithType — a raw encrypted-
object PUT that never registers the object in the bucket's private-forest index.
Result: AI files were readable by exact key but NOT enumerable, so they never appeared
in FxFiles (or the AI's own fula_list_files). See functionland/fula-api#82.

Switch every workspace I/O path to the forest-tracked bindings (the same ones the
FxFiles app already uses):
  - storeFile + applyTaggingToDoc  putEncryptedWithType -> putFlat (upsert + flush)
  - readFile + tag-doc read         getDecrypted        -> getFlat (forest + CID read)
  - listFiles + search              listDecrypted       -> listFilesFromForest
    (listFiles' category narrowing moves to the post-filter; the raw listDecrypted
     prefix-filtered the OBFUSCATED storage keys and returned nothing for this bucket.)

Concurrency: put_object_flat flushes the forest with a conditional PUT; a concurrent
writer makes it 412 (ConcurrentModification). The wasm flush is single-attempt, so
withWorkspaceClient now retries on 412 — re-creating the client drops the stale forest
cache so the next attempt reloads the winner's forest and re-applies the write
(bounded + jittered; read-only bodies never 412).

Requires @functionland/fula-client ^0.6.17 (the build that exports putFlat/getFlat/
listFilesFromForest). Verified: tsc clean + a workerd (vitest-pool-workers) integration
test driving putFlat -> listFilesFromForest -> getFlat against the prod gateway, in the
Worker runtime, round-trips byte-exact.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0161UGEJmTpM6DM2cVZyV6Ev
@ehsan6sha
ehsan6sha merged commit 78cb52c into main Jun 24, 2026
2 checks passed
ehsan6sha added a commit to functionland/FxFiles that referenced this pull request Jun 25, 2026
…rol (1.i/1.ii/1.iii) (#93)

After the Worker writes AI files forest-indexed (functionland/pinning-service#68 +
functionland/fula-api#82), FxFiles can see + move them. Three parts:

P1 (1.i) category map: the MCP writes SINGULAR category segments (ai/document/,
ai/note/, ai/image/ …); FxFiles' category views are PLURAL. _mergeAiWorkspaceInto now
maps them (images<-image+screenshot, documents<-document+note+link, other<-file+other)
and folds the AI categories with no FileCategory of their own into the closest view. A
view with no AI mapping (downloads/archives/starred) short-circuits.

P2 (1.i) cloud-files: the bucket view routes fula-ai-workspace through the workspace
client (workspace secret) instead of the master-KEK listObjects, so the AI files
decrypt + list, tagged sourceBucket for correct open/move routing.

P3 (1.ii/1.iii) move-as-access-control: moving a file INTO the AI bucket re-encrypts it
under the workspace secret + forest-indexes it (grant AI read) under an
ai/<category>/<name> key; moving OUT re-encrypts under the master KEK and removes the
workspace copy (revoke). The revoke is VERIFIED: the re-encrypted master copy must
decrypt BEFORE the AI copy is deleted, then the AI copy is verified gone — "revoke
incomplete" is surfaced loudly otherwise. All re-keying is client-side (the KEK never
leaves FxFiles). New primitives: uploadWorkspaceObject (putFlat), deleteWorkspaceObject
(deleteFlat, which removes the forest entry too).

The move orchestration is factored into a UI-free aiAwareMove() so the security-
critical revoke is unit-tested (grant / verified-revoke / abort-without-delete-on-
failed-verify / normal-move). flutter analyze clean (0 errors); 25 AI unit tests green.


Claude-Session: https://claude.ai/code/session_0161UGEJmTpM6DM2cVZyV6Ev

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant