127.0.0.1 only and takes no key. These steps assume you opened it as described in Deploy Yukta.
Prerequisites: an ADMIN key and the ability to change the server’s settings and restart it. For who can do what and why, read The security model. Every setting named here is listed with its default in Security reference.
Steps
1
Give each caller a named key with a role
Keys are a list in In a container or
yukta.api-keys. Each entry has a name (used in the audit), a key, a role (VIEWER, OPERATOR or ADMIN) and, for the first two, the sessions it may reach:systemd file, use the same list as YUKTA_API_KEYS_0_NAME, YUKTA_API_KEYS_0_KEY, YUKTA_API_KEYS_0_ROLE, YUKTA_API_KEYS_1_... and so on.- A
VIEWERreads. AnOPERATORalso starts, stops and steers runs, and answers input requests. Defining sessions and workflows, changing scheduler limits and reading/actuator/**needADMIN. - A
VIEWERorOPERATORkey with nosessionsis refused at start-up. Write*to allow every session, so a misspeltsessioncan never widen a key. - The same secret in two entries is refused at start-up.
2
Store the digest, not the key
A key that starts with Give the person or system
sha256: followed by 64 hex digits is the SHA-256 digest of the real key. The server then never holds the plain key, and clients still send the plain one.$key, and the server sha256:<the digest>. Keys shorter than 32 characters get a warning in the log.3
Rotate and expire keys
Several keys are valid at once. To rotate with no downtime, add the new key as another entry, move the clients over, then remove the old entry and restart. Set an expiry on a short-lived key:After that instant the key is refused with
401. Revoking a key still needs a settings change and a restart.4
Allow only the commands you mean
PROCESS_EXECUTOR runs nothing until you allow commands. List the executables:useShell) is refused, a node’s workingDir must stay inside the root, and a node cannot set environment variables such as PATH, LD_* or JAVA_TOOL_OPTIONS. The list names an executable, not what it does with its arguments, so it is not a sandbox: never allow a shell or an interpreter such as python3. Use yukta.process.allow-any=true only when every workflow author is fully trusted.5
Limit the hosts a workflow may call
By default A call must pass every list that applies, so a per-workflow or per-session list only narrows what the deployment allows.
HTTP_REQUEST, MCP_TOOL_CALL and NOTIFY_TERMINAL may call any host except link-local and cloud metadata addresses. Narrow it:*.example.com matches subdomains but not example.com. The list works on the name in the URL: it does not look up what a name resolves to, so keep listed names under your control. Per-plugin lists also exist: yukta.http.allowed-hosts and yukta.mcp-client.allowed-hosts.6
Keep secrets out of workflow definitions
A definition that holds a literal secret is refused when it is saved or run. Write a reference and set the value on the server:
decrypted:db.password reads YUKTA_SECRET_DB_PASSWORD: upper-case the name and replace every character that is not a letter or digit with _. Each read is logged with the key name, never the value. Placeholders such as ${env.NAME} read only variables that start with YUKTA_VAR_, a short safe list, or names you list in YUKTA_VARIABLES_ALLOWED; names that contain PASSWORD, SECRET, TOKEN, CREDENTIAL, PRIVATE or KEY stay blocked.7
Read who did what
Control commands (pause, stop, skip, answer an input request, change limits and others) are recorded with the sender: The list is newest first, with whether each command was applied or refused and why. Add
key:<name> for a named key, the supervisor’s id for a supervisor.executionId=<id> to follow one run. The web UI shows the same list on the Control page, on its Audit tab.Verify it worked
Call the server with each key and with none:401 (no key). The second shows the CI key’s name, role and sessions. The third prints 403, because a VIEWER may not change anything. yukta-cli whoami shows the same as the second call. A refusal carries no JSON body: handle it by status code. 401 means no key or a wrong key, 403 means the role or session list does not allow the request, and 429 means the key made too many calls.
Variations that matter
- Behind a proxy with no key. With no key set, the server accepts only the host names
localhost,127.0.0.1and[::1], and a changing request with a foreignOriginis refused with403and no body. List your host withyukta.allowed-hostsandyukta.allowed-origins. The check is off once any key is set, so prefer a key. - Rate limits. Each key may make 1200
/apirequests per minute, and a wrong or missing key counts against the caller’s address (20 per minute), after which the address gets429. Behind a proxy all callers share the proxy’s address. These are JVM system properties (-Dyukta.rate-limit-per-minute,-Dyukta.auth-failures-per-minute);0turns a limit off. Turn the first off for a load test. - The API description. The
prodprofile serves no Swagger UI and no/v3/api-docs. To turn them on behind the key, setSPRINGDOC_API_DOCS_ENABLED=trueandSPRINGDOC_SWAGGER_UI_ENABLED=true. A browser cannot send the key header to the Swagger page, so fetch/v3/api-docswith a key. - LLM and MCP call filters. To refuse prompts or answers that match a pattern, cap prompt size, or restrict the tools a workflow may call, set the
yukta.llm.*andyukta.mcp-client.*filters. They apply to every node whatever the node says. See Security reference. - Supervisors and agent credentials. A supervisor of type
httphas its own credential. See Assign a supervisor. - Remote workers. A worker runs plugins in its own process with its own settings; secure it like the server. See Add remote workers.