profiles: restricted profiles for ordinary users, and a role gate that holds
`is_admin_only` was checked in one place out of nine and was not read from
config.json at all, so all seven profiles were reachable by every account with
role `user`. The flag now lives in config.json — the file is the baseline, a
`profile_overrides` row still wins on top of it — and one predicate,
`admin_only_blocked`, is the single place the rule is expressed. The nine
surfaces that list, switch to, spawn or resolve a profile all consult it:
`POST /sessions`, the WebSocket, switch_profile, list_profiles, the system
prompt's "Available profiles" block, spawn_agent and the Synapse reaction
runner. The prompt cache is now keyed by (profile, role), so a user's prompt
can never be served an admin's profile list.

The seven existing profiles (developer, discuss, dispatcher, modeler_3d,
navi_code, secretary, server_admin) are marked admin-only. Three new ones take
their place for ordinary users: assistant, designer_3d and coder. They share one
native tool set — ssh_exec, peer, reload_tools, create_mcp_server, test_mcp_tool,
image_view and gmail are withheld — and differ only in system prompt, model and
MCP groups. navi-web's raw `request` group, and the whole of gnexus-creds and
tgclient, are withheld too.

MCP per-user keys gain the missing half of the rule: a server that declares a
`user_key` slot is refused to anyone but an admin who has no personal key, and
is left out of their tool list entirely, instead of quietly falling back to the
owner's credential and appearing as a tool that cannot work. The refusal names
the server and points at Settings.

Also closes `GET /agents/prompts`, which served every profile's system prompt to
anyone, with no user dependency at all.

The accepted residual risk is written down in docs/profiles.md: the working
directory is a convention, not a sandbox.
1 parent f9a49ba commit fa339d7ca0975cf1767347522f4c567c998fef42
@Eugene Sukhodolskiy Eugene Sukhodolskiy authored 1 hour ago
Showing 53 changed files
View
README.md
View
docs/api.md
View
docs/auth.md
View
docs/config.md
View
docs/index.md
View
docs/mcp.md
View
docs/profiles.md
View
docs/synapse.md
View
manuals/spawn_agent.md
View
navi/api/routes/admin.py
View
navi/api/routes/agents.py
View
navi/api/routes/sessions.py
View
navi/api/websocket.py
View
navi/core/agent.py
View
navi/core/context_builder.py
View
navi/core/tool_utils.py
View
navi/mcp/keystore.py
View
navi/mcp/manager.py
View
navi/mcp/tools.py
View
navi/profiles/assistant/config.json 0 → 100644
View
navi/profiles/assistant/system_prompt.txt 0 → 100644
View
navi/profiles/base.py
View
navi/profiles/coder/config.json 0 → 100644
View
navi/profiles/coder/system_prompt.txt 0 → 100644
View
navi/profiles/designer_3d/config.json 0 → 100644
View
navi/profiles/designer_3d/system_prompt.txt 0 → 100644
View
navi/profiles/developer/config.json
View
navi/profiles/discuss/config.json
View
navi/profiles/dispatcher/config.json
View
navi/profiles/loader.py
View
navi/profiles/modeler_3d/config.json
View
navi/profiles/navi_code/config.json
View
navi/profiles/secretary/config.json
View
navi/profiles/server_admin/config.json
View
navi/synapse/reactions.py
View
navi/tools/_internal/base.py
View
navi/tools/list_profiles.py
View
navi/tools/spawn_agent.py
View
navi/tools/switch_profile.py
View
navi/tools/test_mcp_tool.py
View
tests/integration/conftest.py
View
tests/integration/test_api_routes.py
View
tests/unit/core/test_context_builder.py
View
tests/unit/core/test_mcp_scope_tool_list.py 0 → 100644
View
tests/unit/core/test_synapse_reactions.py
View
tests/unit/mcp/test_manager_byok.py
View
tests/unit/profiles/test_profile_loader.py
View
tests/unit/test_mcp.py
View
tests/unit/tools/test_list_profiles.py 0 → 100644
View
tests/unit/tools/test_mcp_meta_tools.py
View
tests/unit/tools/test_switch_profile.py
View
webclient/src/components/settings/McpKeysPanel.vue
View
webclient/tests/unit/components/settings/mcpKeysPanel.test.js