Projects give each user a persistent workspace and define the directories filesystem and code-execution tools may access. Switching Projects changes the active boundary for read, write, edit, search and listing operations.
How the boundary works​
Each project is a dedicated folder under the signed-in user's own storage:
When a project is active, that folder replaces the user's allowed directories. Every path a filesystem tool receives is normalized to a full path and checked against it, so .. segments and absolute paths can't escape.
With no active project, AI Chat falls back to the explicitly configured Tools.AllowedDirectories - which is empty by default, and is the only thing that enables filesystem tools in the first place:
services.AddPlugin(new ChatFeature {
Tools = {
EnableFilesystemTools = true,
AllowedDirectories = ["/srv/shared-workspace"],
},
});
INFO
Projects narrow access; they don't grant it. EnableFilesystemTools and EnableCodeExecution remain off by default and must be enabled by the host before any of this applies. See Tools.
Project storage​
projects.json in the user's projects/ folder is the file-backed project list, and the active project is a user preference:
[
{ "name": "Marketing Site", "folder": "marketing-site", "publish": "dist" },
{ "name": "Quarterly Report", "folder": "quarterly-report" }
]
| Key | Purpose |
|---|---|
name |
Display name |
folder |
Folder name, defaulting to a kebab-case slug of name |
publish |
Build output directory, relative to the project folder |
"My App (v2)" slugs to my-app-v2. The publish path is coerced to a relative path inside the project folder - absolute paths, a leading /, a redundant {folder}/ prefix and any .. segments are all reduced away, and the project root is "".
Applying a project on sign-in​
The projects extension registers a setup-user handler, so the first request from each user applies their active project's directory automatically:
Projects [alice] quarterly-report: /srv/app/App_Data/chat/user/alice/projects/quarterly-report
What projects hold​
Projects are ordinary folders, so they can hold anything an Agent produces:
- Generated websites and single-page apps
- Games and interactive demos
- Reports, plans and documents
- Source code
They're also the natural collaboration unit for Agent Profiles. The built-in Planner → Coder workflow uses one: the Planner writes PLAN.md into the active project, and the Coder profile's action only appears once that file exists.
Publishing​
When the publish extension is enabled, a project's build folder can be published as a tar.gz to a remote llms.py site:
services.AddPlugin(new ChatFeature {
Publish = { Enabled = true },
});
Publishing is disabled by default in ServiceStack AI Chat, so each host decides whether content may leave its application boundary. Connection config is stored per user at App_Data/chat/user/{user}/publish/config.json, and the publish dialog's folder browser is confined to the project folder - every path in and out is relative to it, so the UI never sees a server path.
The extension can also publish selected chat threads and individual gallery media items.
API​
The publish extension adds:
Disabling projects​
services.AddPlugin(new ChatFeature {
DisableExtensions = ["projects", "publish"],
});
With projects disabled, filesystem tools fall back to Tools.AllowedDirectories for every user - appropriate when a single shared workspace is what you want, or when you've disabled filesystem tools entirely.
Operational guidance​
- Keep
EnableCodeExecutionoff unless you need it; a project folder bounds file access butrun_bashstill runs on your host. - Consider mounting
App_Data/chat/useron a volume with a quota so a runaway Agent can't fill the disk. - Typst's root restriction and the project path check limit file access but are not operating-system sandboxes. Untrusted workloads belong in a container or dedicated worker.