Filesystem
What you’ll build: A fishbowl instance with a custom overlay filesystem — a writable layer on top of a read-only base, plus a union mount combining two servers at one path.
Prerequisites: Hello World →
Mount a fileserver
Section titled “Mount a fileserver”Every path in fishbowl is served by a fileserver. The default is memoryFS — an in-memory tree. You can mount additional fileservers at any path.
import { Unix, memoryFS } from '@fishnet/core'import { stdSystem } from '@fishnet/core/presets'import { nodeRuntime } from '@fishnet/runtime/platform/node'
const extra = memoryFS()extra.writeFile('/hello.txt', 'from extra fs')
const image = Unix() .use(stdSystem()) .use({ mounts: [{ path: '/mnt/extra', server: extra }], }) .build()
const instance = await image.boot(nodeRuntime())// cat /mnt/extra/hello.txt → "from extra fs"import { Unix, memoryFS } from '@fishnet/core'import { stdSystem } from '@fishnet/core/presets'import { browserRuntime } from '@fishnet/runtime/platform/browser'
const extra = memoryFS()extra.writeFile('/hello.txt', 'from extra fs')
const image = Unix() .use(stdSystem()) .use({ mounts: [{ path: '/mnt/extra', server: extra }], }) .build()
const instance = await image.boot(browserRuntime())Create an overlay
Section titled “Create an overlay”An overlay filesystem adds a writable layer on top of a read-only base. Writes go to the upper layer; reads fall through to the lower layer if the upper layer doesn’t have the file.
import { Unix, memoryFS, overlayFS } from '@fishnet/core'import { stdSystem } from '@fishnet/core/presets'import { nodeRuntime } from '@fishnet/runtime/platform/node'
const base = memoryFS()base.writeFile('/config.json', '{"debug":false}')base.freeze() // make read-only
const upper = memoryFS()const overlay = overlayFS(upper, base)
const image = Unix() .use(stdSystem()) .use({ mounts: [{ path: '/etc', server: overlay }] }) .build()
const instance = await image.boot(nodeRuntime())// Writing /etc/config.json writes to `upper`, not `base`import { Unix, memoryFS, overlayFS } from '@fishnet/core'import { stdSystem } from '@fishnet/core/presets'import { browserRuntime } from '@fishnet/runtime/platform/browser'
const base = memoryFS()base.writeFile('/config.json', '{"debug":false}')base.freeze()
const upper = memoryFS()const overlay = overlayFS(upper, base)
const image = Unix() .use(stdSystem()) .use({ mounts: [{ path: '/etc', server: overlay }] }) .build()
const instance = await image.boot(browserRuntime())Union mounts
Section titled “Union mounts”Union mounts combine multiple fileservers at a single path. Reads fall through in order; writes go to the first server.
Starting from the instance in the previous section (with /mnt/extra already mounted):
# In the shell — mount a second server alongside the first:bind -b /mnt/extra /workspace # prepend extra to /workspacebind -a /mnt/other /workspace # append other to /workspacels /workspace # shows files from all three serversNext steps
Section titled “Next steps”- Containers → — Combine overlays and namespaces into isolated environments
Further reading
Section titled “Further reading”- Fileservers architecture → — Protocol, memoryFS, overlayFS internals
- Namespaces → — Per-process mount tables, fork semantics
- srvFS → — How fileservers are posted and discovered