Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -76,16 +76,16 @@ RUN echo $TZ > /etc/timezone && \
# Install yq (architecture-aware)
RUN ARCH=$(uname -m) && \
if [ "$ARCH" = "x86_64" ]; then ARCH="amd64"; elif [ "$ARCH" = "aarch64" ]; then ARCH="arm64"; fi && \
curl -sL https://github.com/mikefarah/yq/releases/download/v4.50.1/yq_linux_${ARCH}.tar.gz | tar xz && \
curl -sL https://github.com/mikefarah/yq/releases/download/v4.52.2/yq_linux_${ARCH}.tar.gz | tar xz && \
mv yq_linux_${ARCH} /usr/bin/yq && \
rm -rf /tmp/*

# Install Google Cloud SDK (architecture-aware)
RUN ARCH=$(uname -m) && \
if [ "$ARCH" = "x86_64" ]; then \
curl -sSL "https://dl.google.com/dl/cloudsdk/channels/rapid/downloads/google-cloud-sdk-551.0.0-linux-x86_64.tar.gz" -o google-cloud-sdk.tar.gz; \
curl -sSL "https://dl.google.com/dl/cloudsdk/channels/rapid/downloads/google-cloud-sdk-556.0.0-linux-x86_64.tar.gz" -o google-cloud-sdk.tar.gz; \
elif [ "$ARCH" = "aarch64" ]; then \
curl -sSL "https://dl.google.com/dl/cloudsdk/channels/rapid/downloads/google-cloud-sdk-551.0.0-linux-arm.tar.gz" -o google-cloud-sdk.tar.gz; \
curl -sSL "https://dl.google.com/dl/cloudsdk/channels/rapid/downloads/google-cloud-sdk-556.0.0-linux-arm.tar.gz" -o google-cloud-sdk.tar.gz; \
fi && \
tar -xzf google-cloud-sdk.tar.gz && \
./google-cloud-sdk/install.sh -q && \
Expand Down
43 changes: 38 additions & 5 deletions docs/authorization.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ Use this when you need to:

- Credentials can be provided via env vars.
- Secrets can be JSON, Base64, or file paths.
- Secret references are resolved from provider paths in `worker.yaml` and matching runtime env vars.
- Authorization cleanup is controlled by `ACTORS_CLEANUP` (enabled by default).

## Examples

Expand All @@ -30,17 +32,17 @@ Use this when you need to:

```json
{
"client_id": "CLIENT_ID",
"client_secret": "CLIENT_SECRET",
"tenant_id": "TENANT_ID",
"subscription_id": "SUBSCRIPTION_ID"
"clientId": "CLIENT_ID",
"clientSecret": "CLIENT_SECRET",
"tenantId": "TENANT_ID",
"subscriptionId": "SUBSCRIPTION_ID"
}
```

### Base64 Format

```bash
echo -n '{"client_id":"CLIENT_ID","client_secret":"CLIENT_SECRET","tenant_id":"TENANT_ID","subscription_id":"SUBSCRIPTION_ID"}' | base64
echo -n '{"clientId":"CLIENT_ID","clientSecret":"CLIENT_SECRET","tenantId":"TENANT_ID","subscriptionId":"SUBSCRIPTION_ID"}' | base64
```

### File Path
Expand All @@ -49,10 +51,41 @@ echo -n '{"client_id":"CLIENT_ID","client_secret":"CLIENT_SECRET","tenant_id":"T
AZURE_CREDS="/path/to/azure_credentials.json"
```

### Provider-Specific Credential Keys

- Azure (`AZURE_CREDS`): `clientId`, `clientSecret`, `tenantId`, `subscriptionId`
- AWS (`AWS_CREDS`): `AccessKeyId`, `SecretAccessKey`, optional `SessionToken`
- GCP (`GCP_CREDS`): standard GCP credential JSON (service account or other `gcloud --cred-file` compatible JSON)

### Security Scenario: Long-Lived Credentials

Example concern:
- A static service principal secret is stored in CI and reused for months.
- If leaked, an attacker can keep resolving secrets from the same vault scope until rotation.

How worker design can mitigate:
- Fetch only referenced secrets at startup (for example `azure/<vault>/<secret>`).
- Remove local auth artifacts after setup when `ACTORS_CLEANUP=true`.
- Override secret references per environment at deploy time for safer rotation workflows.

How worker design can exacerbate:
- Resolved secrets are exported to process environment and can live for the container lifetime.
- Services can leak secrets if scripts print env vars or run with verbose shell tracing.
- A single broad credential can unlock multiple vault scopes in one worker instance.

### Recommended Credential Posture

- Prefer short-lived credentials or federation-based identity flows over long-lived static secrets.
- Grant least-privilege access to only the required secret scopes.
- Rotate provider credentials and secret values regularly.
- Split high-trust workloads into separate worker deployments when hard isolation is required.

## Common Pitfalls

- Using relative credential paths in production.
- Storing secrets in version control.
- Using long-lived provider credentials with broad access scope.
- Reusing one credential principal for unrelated services that require isolation.

## Related Docs

Expand Down
64 changes: 59 additions & 5 deletions docs/runtime/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,11 @@ Use this when you need to:

- Runtime-only config: `/home/udx/.config/worker/worker.yaml`.
- Deployment env vars override `worker.yaml` values.
- Secret references use `provider/vault/secret` format.
- Secret references use `provider/<scope>/<name>` format.
- Provider reference formats:
- `azure/<key-vault-name>/<secret-name>`
- `gcp/<project-id>/<secret-name>`
- `aws/<secret-name>/<region>`

## Examples

Expand All @@ -30,11 +34,25 @@ config:
AZURE_CLIENT_ID: "12345678-1234-1234-1234-1234567890ab"
AWS_REGION: "us-west-2"
secrets:
DB_PASSWORD: "aws/prod/db_password"
DB_PASSWORD: "aws/db-password/us-west-2"
API_KEY: "azure/kv-prod/api-key"
```

### Secret References in Env
### Static Secret Injection (`API_KEY`) and External Secret Resolution (`DB_PASSWORD`)

```yaml
kind: workerConfig
version: udx.io/worker-v1/config
config:
env:
API_KEY: "dev-only-static-key"
secrets:
DB_PASSWORD: "azure/kv-prod/db-password"
```

`API_KEY` is injected as-is. `DB_PASSWORD` is resolved from the provider and then exported as an environment variable.

### Secret References in `config.env`

```yaml
config:
Expand All @@ -44,11 +62,47 @@ config:
LOG_LEVEL: "info"
```

If an `env` value matches a secret reference format, the worker resolves it at startup.

### Separate Secret Scopes for Different Services

Use separate variable names in `worker.yaml`, then consume the right variable in each service:

```yaml
# worker.yaml
kind: workerConfig
version: udx.io/worker-v1/config
config:
secrets:
SERVICE_A_DB_PASSWORD: "azure/kv-service-a/db-password"
SERVICE_B_DB_PASSWORD: "azure/kv-service-b/db-password"
```

```yaml
# services.yaml
kind: workerService
version: udx.io/worker-v1/service
services:
- name: "serviceA"
command: "bash -lc 'exec /home/udx/bin/service_a.sh'"
envs:
- "SERVICE_NAME=serviceA"

- name: "serviceB"
command: "bash -lc 'exec /home/udx/bin/service_b.sh'"
envs:
- "SERVICE_NAME=serviceB"
```

`serviceA` should read `SERVICE_A_DB_PASSWORD`, and `serviceB` should read `SERVICE_B_DB_PASSWORD`.
For strict isolation boundaries, run separate worker instances with separate identities.

### Runtime Environment and Precedence

1. **Deployment environment variables** (highest priority)
2. `worker.yaml` `config.secrets`
3. `worker.yaml` `config.env`
2. Deployment environment variables containing secret references (resolved at startup)
3. `worker.yaml` `config.secrets`
4. `worker.yaml` `config.env`

Example override:

Expand Down
65 changes: 50 additions & 15 deletions docs/runtime/services.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ Use this when you need to:

- Runtime-only: it lives inside the container at `/home/udx/.config/worker/`.
- Image selection happens at deployment time (see `docs/deploy/README.md`).
- Each service is configured with a single `command` string (there is no `args` field in `services.yaml`).

## Examples

Expand All @@ -40,50 +41,84 @@ kind: workerService
version: udx.io/worker-v1/service
services:
- name: "logger"
command: "bash -c 'echo "[startup] logger"; while true; do echo "[tick] $(date)"; sleep 5; done'"
command: >-
bash -lc 'echo "[startup] logger";
while true; do echo "[tick] $(date)"; sleep 5; done'
autostart: true
autorestart: true
```

Then view output:

```bash
worker service logs logger
worker service logs logger --tail 100 --follow
worker service errors logger --tail 100 --follow
```

Example scripts: `src/examples/simple-service/`

### Multiple Services
### Two Independent Services (Concurrent)

```yaml
kind: workerService
version: udx.io/worker-v1/service
services:
- name: "api-server"
command: "npm start"
- name: "serviceA"
command: >-
bash -lc 'echo "starting $SERVICE_NAME"; exec /home/udx/bin/service_a.sh'
autostart: true
autorestart: true
stopasgroup: true
killasgroup: true
envs:
- "PORT=3000"
- "NODE_ENV=production"
- "SERVICE_NAME=serviceA"
- "LOG_LEVEL=info"

- name: "worker-queue"
command: "python worker.py"
- name: "serviceB"
command: >-
bash -lc 'echo "starting $SERVICE_NAME"; exec /home/udx/bin/service_b.sh'
autostart: true
autorestart: true
envs:
- "QUEUE_URL=redis://localhost:6379"
- "SERVICE_NAME=serviceB"
- "LOG_LEVEL=warn"
```

### Passing Command-Line Arguments to a Script

- name: "monitoring"
command: "./monitor.sh"
ignore: true
```yaml
kind: workerService
version: udx.io/worker-v1/service
services:
- name: "job-runner"
command: >-
/home/udx/bin/job-runner.sh
--config-path=/etc/app/config.json
--mode=sync
--retry=3
autostart: true
autorestart: true
```

### Reading Environment Variables in the Executed Command

```yaml
kind: workerService
version: udx.io/worker-v1/service
services:
- name: "print-service-name"
command: >-
bash -lc 'echo "SERVICE_NAME=$SERVICE_NAME"; exec /home/udx/bin/start.sh'
autostart: true
autorestart: true
envs:
- "SERVICE_NAME=worker-api"
```

## Common Pitfalls

- Using `services.yaml` to select the image (use `deploy.yml` instead).
- Forgetting to mount `services.yaml` into the container.
- Expecting an `args` field in `services.yaml` (put arguments directly in `command`).
- Putting provider references (for example `azure/...`) in `services.yaml` `envs`.

## Related Docs

Expand Down