Use this skill when frontend code needs to read or write app files, inspect the current user, discover files by pattern, or call backend endpoints from the browser.
Core Frontend API Surface
The shared frontend runtime exposes authenticated backend helpers through space.api.
Current wrapped helpers include:
await space.api.fileList(pathOrOptions, recursive?)await space.api.fileRead(pathOrFiles, encoding?)await space.api.fileWrite(pathOrFiles, content?, encoding?)await space.api.fileDelete(pathOrPaths)await space.api.fileCopy(pathOrEntries, toPath?)await space.api.fileMove(pathOrEntries, toPath?)await space.api.fileInfo(pathOrOptions)space.api.folderDownloadUrl(pathOrOptions)await space.api.gitHistoryList(pathOrOptions, limit?)await space.api.gitHistoryDiff(pathOrOptions, commitHash?, filePath?)await space.api.gitHistoryPreview(pathOrOptions, commitHash?, operation?, filePath?)await space.api.gitHistoryRollback(pathOrOptions, commitHash?)await space.api.gitHistoryRevert(pathOrOptions, commitHash?)await space.api.userSelfInfo()await space.api.call("endpoint_name", { method, query, body, headers, signal })
fileRead(...) accepts one logical path, one { path, encoding? } entry, or a files batch. The frontend wrapper now coalesces same-tick reads into one /api/file_read request and re-slices the returned files back to each caller, retrying individually when a combined batch fails so shorthand paths and optional missing-file reads still behave like standalone calls. Explicit batching is still best when you already have the file list, but small independent reads no longer fan out 1:1 by default.
fileWrite(...) still supports the simple replacement form fileWrite(path, content, encoding?), but object-form writes also support incremental updates: { path, content, operation: "append" }, { path, content, operation: "prepend" }, or { path, content, operation: "insert", line | before | after }. Insert writes accept exactly one anchor, use the first literal before or after match, treat line as a 1-based insertion point, and require utf8. Batch writes may set those fields per entry or once at the top level as defaults. Prefer these incremental write modes when you only need to add or place text instead of rereading and rewriting the whole file.
Use space.api.folderDownloadUrl(...) when the browser should trigger a regular authenticated folder download without buffering the ZIP file into frontend memory first.
When a UI needs user-visible download failure feedback without fetching the archive blob into memory, preflight the request with space.api.fileInfo(...) for files or space.api.call("folder_download", { method: "HEAD", query: { path } }) for folders before starting the browser download.
fileList(...) accepts { access: "write" } or { writableOnly: true } when discovery must be limited to writable paths. Use { gitRepositories: true, access: "write" } or space.api.call("file_paths", { method: "POST", body: { patterns: ["**/.git/"], gitRepositories: true, access: "write" } }) to discover writable local-history owner roots; the server returns paths such as L1/team/ and L2/alice/, not .git metadata.
gitHistoryList(...), gitHistoryDiff(...), gitHistoryPreview(...), gitHistoryRollback(...), and gitHistoryRevert(...) are available only when the backend runtime has CUSTOMWARE_GIT_HISTORY=true. They operate on writable owner roots such as ~, L2/<user>/, or L1/<group>/; list supports limit, offset, and fileFilter, preview returns affected files and optional operation-specific patches for travel or revert, diff reads one commit-file patch, rollback requires write permission and preserves ignored L2 auth files plus forward-travel refs, and revert creates a new inverse commit. The first-party #/time_travel page defaults to the authenticated user's ~ history and can switch to write-accessible L1 or L2 history roots through the repository picker.
Logical Path Rules
- Use logical app-rooted paths such as
L2/alice/user.yaml, not disk paths. ~and~/...target the authenticated user'sL2/<username>/...path.- These logical paths do not change when writable storage moves under
CUSTOMWARE_PATH. fileWrite(".../")creates a directory because the path ends with/..gitmetadata under writable owner roots is server infrastructure and is blocked from app-file reads, writes, direct fetches, and indexed discovery.
Discovery Rules
- Use permission-aware APIs, not ad hoc browser path guesses.
- Use
space.api.call("file_paths", { method: "POST", body: { patterns: [...] } })for indexed glob discovery; addaccess: "write"for writable-only results. - Server-side
file_pathsdiscovery is shard-scoped to readable or writablefile_indexowner shards; the endpoint ensures the current user's full L2 shard when needed, so callers should pass precise patterns and rely on the endpoint instead of trying to enumerate all app paths in browser code. - Use
space.api.call("module_list", ...)only when you need module inventory metadata rather than raw file paths. - Use
space.api.call("extensions_load", ...)when the browser needs module-ownedext/...assets resolved with layered override behavior, such as HTML adapters or JS hooks. KeepmaxLayerat the top level of the call, and when batching grouped lookups send orderedpatternsentries and read grouped results back in that same order. - Use
file_pathsplusfileRead(...)for readable module metadata files such asext/panels/*.yamlwhen you only need logical file discovery and file contents rather than theextensions_loadresponse shape.
Storage Rules
- Browser storage is for small non-authoritative UI state.
- Persistent user or group state should live in app files or explicit backend APIs.
- Use
space.configonly for frontend-exposed runtime params, not for general persistence. - Browser code may update the current user's
~/user.yamldirectly when changing browser-owned metadata such asfull_name, but password rotation must stay backend-owned throughspace.api.call("password_change", ...)instead of writing~/meta/password.json; that endpoint also clears the current login's persisteduserCryptobrowser state.
Identity Snapshot
space.api.userSelfInfo()returns{ username, fullName, groups, managedGroups, sessionId, userCryptoKeyId, userCryptoState }.- Treat
app/as the frontend repo tree andserver,commands, andpackagingas read-only from this frontend skill set. - The current user may always write
L2/<username>/andL2/<username>/mod/. - The current user may write
L1/<group>/andL1/<group>/mod/for each entry inmanagedGroups. - If
groupsincludes_admin, the user may write anyL1/<group>/...orL2/<user>/...path exceptL0, which remains firmware-only. - Readable group roots still follow group membership and layer rules; use
development/layers-ownershipwhen you need the full read-resolution model.
Boundary Rule
- This skill is for consuming backend APIs from the frontend.
- Do not change backend handlers from this skill set; load
development/backend-referencewhen you need the read-only backend model behind these APIs. - Prefer frontend logic plus existing
space.apihelpers over asking for new backend endpoints. - Ask for backend permission only when the browser cannot safely enforce the required security, integrity, cross-user, or stability boundary on its own.
Mandatory Doc Follow-Up
- If the frontend API surface, app-file path rules, or indexed discovery behavior change, update the mirrored docs and the
developmentskill subtree in the same session.

