| 文件 | 最后提交记录 | 最后更新时间 |
|---|---|---|
| 1 个月前 | ||
| 1 个月前 | ||
| 1 个月前 | ||
| 1 个月前 | ||
| 1 个月前 | ||
| 1 个月前 | ||
| 1 个月前 | ||
| 1 个月前 | ||
| 1 个月前 |
secafs-chat — OpenClaw plugin
An external, third-party OpenClaw plugin that gives every conversation its
own openGauss/PostgreSQL-backed, FUSE-mounted workspace, with the agent running
inside that mount. It imports only the public openclaw/plugin-sdk/* surface
and the gateway protocol, so the OpenClaw checkout stays pristine.
Full deploy guide: ../README.md.
Build
npm install # esbuild, vitest, types
npm run build # → dist/index.js (bundled, ESM, packages external)
npm test # 116 unit tests (re-link node_modules/openclaw first — see below)
npm run typecheck # tsc against the openclaw SDK types
npm installoverwrites thenode_modules/openclawsymlink used by typecheck/tests.npm run builddoesn't need it; beforenpm test/typecheckre-link:ln -sfn ../../../../openclaw node_modules/openclaw.
Install into a gateway
Point the gateway config at this directory — do not copy files into the
OpenClaw tree, and do not use openclaw plugins install (it hangs on this
version before recording the install). In ~/.openclaw/openclaw.json:
plugins: {
load: { paths: ["<abs>/secafs/integrations/openclaw/plugin"] },
entries: { "secafs-chat": { enabled: true, config: {
manageDaemon: false,
socketPath: "/home/<you>/.secafs/run/secafs.sock",
mountRoot: "/home/<you>/.secafs/mounts",
} } },
}
openclaw.plugin.json declares activation.onStartup: true — required, or
the gateway discovers the plugin but never calls register() and secafs.*
methods return "unknown method".
Config (plugins.entries.secafs-chat.config)
| key | default | meaning |
|---|---|---|
socketPath |
$XDG_RUNTIME_DIR/secafs/secafs.sock |
daemon Unix socket |
mountRoot |
$XDG_STATE_HOME/secafs/mounts |
per-conversation mount parent |
manageDaemon |
false |
gateway spawns secafs serve api itself (then postgresUrl is required) |
postgresUrl |
— | DSN, only when manageDaemon: true |
idleScanSeconds |
2 |
mount-keeper / idle-scan tick (also the recovery tick) |
idleUnmountSeconds |
0 (off) |
idle auto-unmount; off by default (avoids the vanished-workspace trap) |
enableRollbackUI |
true |
register secafs.rollback.* and snapshot at turn boundaries |
Gateway methods
- Status:
secafs.status - Lifecycle:
secafs.session.create(alias) ·.open·.close(unmount, keep data) ·.destroy(delete volume + chat + transcripts) ·.list·.rename - Files:
secafs.tree·secafs.fs.read·secafs.fs.write - Rollback:
secafs.rollback.setEnabled·.list·.restore·.snapshot(eager per-turn snapshot for the per-message ⏪ button) - Portability:
secafs.session.export·secafs.session.import(always a fresh copy; cross-instance capable)
Requirements
- A reachable
secafs serve apidaemon (thesecafsRust binary onPATH; built from the sibling SecAFS repocli/workspace). - openGauss / PostgreSQL reachable by that daemon.
- Linux with
/dev/fuse; in this setup mounts run inside a user namespace (nosudo/fusermount3) — see../bridge/run-stack.sh.
How it works
- Path C anchors the agent's cwd to the FUSE mount using only upstream
seams: the plugin writes
spawnedCwd/spawnedWorkspaceDir/spawnedByto the session entry (all key forms), so the agent run honors them — no OpenClaw core change. See../INTEGRATION.md. - Mount-keeper (idle scanner): every tick it remounts store-mounted sessions
the daemon lost (crash/respawn) or whose kernel mount went stale, keeping the
<5sremount SLO and preventingWorkspaceVanishedError. - Rollback rides SecAFS's copy-on-write snapshots + undo log;
restorerolls back the volume and truncates the chat transcript to that point. - Session-store writes go through upstream's
updateSessionStore(the same writer queue the gateway uses) so plugin and gateway never race. - All session state the plugin owns (
kind,mountState,alias,secafsRollback) persists underpluginExtensions["secafs-chat"], keeping the on-diskSessionEntrywithin the upstream-typed shape.