Repository navigation
Security
| Secret | Where it comes from | Where it goes |
|---|---|---|
HULY_PASSWORD |
environment | account service login() only |
Account token (HULY_TOKEN or from login) |
environment / account service | account service only (discovery, selectWorkspace) |
| Workspace tokens |
selectWorkspace() (or a workspace-scoped HULY_TOKEN) |
WebSocket transactor, account service (getWorkspaceMembers), front /files / datalake, always in a header or the WebSocket handshake, never in a URL |
Tokens are kept in process memory only. The server writes nothing to disk. The only exception is the setup wizard, which writes HULY_TOKEN to the local .env. .env is listed in .gitignore and .dockerignore.
Tool results go to the AI client and usually end up in chat transcripts. They must never contain credentials.
Before the fix (commit 9e64a2d), get_document with HULY_FRONT_URL set built
{HULY_FRONT_URL}/files?file=<blob>&workspace=<uuid>&token=<workspace token>
and printed this URL as "Content URL" in the tool result. That exposed the workspace token to the agent and the transcript.
Now:
- The content is fetched with
GET {HULY_FRONT_URL}/files?file=<blob>&workspace=<uuid>and the headerAuthorization: Bearer <workspace token>. - The printed Content URL has no credentials and carries the note "requires a workspace token as Bearer header".
- The content is still fetched by the server and shown in the result, as before.
- The token used is the one of the workspace named in the call.
These fetched description blobs with the same token= query parameter (commit c2adba5). The URL wasn't printed, but a token in a query string ends up in reverse-proxy and access logs. They now use the Bearer header too.
- Every HTTP request to
/filesor the datalake sends the workspace token asAuthorization: Bearer …. - No URL built in
src/containstoken=.
Regression tests in test/tools.test.ts assert for get_document, get_issue and get_organization:
- the request URL contains no
token=and no token, - the Bearer header carries the correct workspace's token,
- the tool result contains neither
token=nor the token (success, HTTP error and network error paths).
They fail against the old code.
- Error messages name workspaces (slug, name, UUID), never tokens or passwords.
- The setup wizard prints workspace names/slugs/IDs and "Token saved to .env", never the token.
- Workspace UUIDs and blob references appear in tool output. They are identifiers, not credentials.
- Writes require an explicit
workspace. There is no implicit, default or last-used workspace (see Multi-Workspace). - Blank
workspacevalues are rejected, not treated as "omitted". - Each call reads and writes through the connection and token of its own workspace only.
-
HULY_WORKSPACEcan additionally restrict a server to a single workspace.
The Docker image exposes the server over HTTP via mcp-proxy without any client authentication. mcp-proxy starts one server process that is shared by all HTTP clients. So:
- Anyone who can reach port 8000 acts as the configured Huly account in all of its workspaces (unless restricted with
HULY_WORKSPACE). - Do not expose the port publicly. Put it behind a reverse proxy with authentication (e.g. OAuth2 proxy, mTLS, VPN or a private network).
- Use a dedicated Huly account with only the workspaces and roles the remote clients need.
See Docker Deployment.
- Prefer email + password or an account token from
npm run setupover browser-copied tokens. - Keep
.envout of version control (default) and restrict its file permissions. - Rotate the password/token if a transcript or log may have contained a token from a version before the fixes above.