Skip to content

Security

JoKeks2023 edited this page Sep 28, 2026 · 1 revision

Security

Credentials and tokens handled by the server

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.

No workspace tokens in tool results

Tool results go to the AI client and usually end up in chat transcripts. They must never contain credentials.

Fixed: get_document Content URL

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 header Authorization: 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.

Fixed: get_issue / get_organization

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.

Rule: auth headers, never credentials in URLs

  • Every HTTP request to /files or the datalake sends the workspace token as Authorization: Bearer ….
  • No URL built in src/ contains token=.

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.

Errors and logs

  • 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.

Workspace isolation as a safety property

  • Writes require an explicit workspace. There is no implicit, default or last-used workspace (see Multi-Workspace).
  • Blank workspace values are rejected, not treated as "omitted".
  • Each call reads and writes through the connection and token of its own workspace only.
  • HULY_WORKSPACE can additionally restrict a server to a single workspace.

Remote deployment

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.

Recommendations

  • Prefer email + password or an account token from npm run setup over browser-copied tokens.
  • Keep .env out 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.

Clone this wiki locally