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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ init.lisp
# Build artifacts
result
result-*
__pycache__/
stack-test-artifacts/

# Local deployment configuration and secrets
.env
Expand Down
27 changes: 16 additions & 11 deletions DOCKER.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ Nix flake:
- Apache CouchDB 3.5.2
- Clouseau 3.3.0 on Java 21
- RabbitMQ 4.3.4 with the management plugin
- Valkey 9.0.1 with authenticated AOF persistence

CouchDB is pinned by its linux/amd64 manifest digest and Nix content hash.
Clouseau is pinned by its release artifact hash. Compose never builds or pulls
Expand All @@ -24,7 +25,7 @@ a project-owned image.
Build every project-owned image without loading it:

```bash
nix build .#star-server-image .#couchdb-image .#clouseau-image .#rabbitmq-image
nix build .#star-server-image .#couchdb-image .#clouseau-image .#rabbitmq-image .#valkey-image
```

Build the images, merge their archives, and load them into Docker:
Expand All @@ -34,8 +35,8 @@ nix run .#load-images
```

The loaded tags are `starintel/server:0.1.0`,
`starintel/couchdb:3.5.2`, `starintel/clouseau:3.3.0`, and
`rabbitmq:4.3.4-management`.
`starintel/couchdb:3.5.2`, `starintel/clouseau:3.3.0`,
`rabbitmq:4.3.4-management`, and `starintel/valkey:9.0.1`.

## Configure secrets

Expand All @@ -49,6 +50,7 @@ openssl rand -base64 32 > secrets/couchdb_password
openssl rand -base64 48 > secrets/couchdb_secret
openssl rand -hex 24 | tr '[:lower:]' '[:upper:]' > secrets/erlang_cookie
openssl rand -base64 32 > secrets/rabbitmq_password
openssl rand -base64 32 > secrets/valkey_password
chmod 0600 secrets/*
```

Expand Down Expand Up @@ -77,6 +79,7 @@ The default host bindings are:
- CouchDB: `http://127.0.0.1:5984`
- RabbitMQ AMQP: `127.0.0.1:5672`
- RabbitMQ management: `http://127.0.0.1:15672`
- Valkey: `127.0.0.1:6379`

Clouseau is internal-only. CouchDB and Clouseau share an Erlang cookie and
CouchDB addresses the search node as
Expand All @@ -95,7 +98,7 @@ docker compose down --volumes
```

The destructive command above removes the `couchdb_data`, `clouseau_index`,
and `rabbitmq_data` volumes.
`rabbitmq_data`, and `valkey_data` volumes.

## Search initialization and verification

Expand Down Expand Up @@ -153,11 +156,13 @@ queries match.

## Persistence and backup

Named volumes preserve CouchDB documents, Clouseau indexes, and RabbitMQ state
across container replacement. Back up CouchDB through its HTTP API rather than
copying a live volume. The export command in the migration section includes
attachments; store its output in encrypted backup storage. Back up every
application database and test a restore regularly.
Named volumes preserve CouchDB documents, Clouseau indexes, RabbitMQ state, and
the Valkey AOF across container replacement. The Valkey entrypoint reads the
root-only password secret, installs only its SHA-256 ACL verifier, and drops to
UID/GID 65532 before starting the server. Back up CouchDB through its HTTP API
rather than copying a live volume. The export command in the migration section
includes attachments; store its output in encrypted backup storage. Back up
every application database and test a restore regularly.

The Clouseau volume improves restart time but does not need an independent
backup because its indexes can be rebuilt from CouchDB.
Expand All @@ -167,9 +172,9 @@ backup because its indexes can be rebuilt from CouchDB.
1. Read the CouchDB and Clouseau release notes and compatibility requirements.
2. Export every CouchDB database.
3. Update the pinned versions, digest, and hashes in `nix/images.nix`
(CouchDB, Clouseau, and RabbitMQ).
(CouchDB, Clouseau, RabbitMQ, and Valkey).
4. Update the matching image tags in `docker-compose.yml`.
5. Run `nix build` for all three images and `./scripts/stack-test.sh`.
5. Build all images and run `./scripts/stack-test.sh`.
6. Load the new images and recreate the stack with
`docker compose up --detach --wait`.
7. Verify health, document counts, and representative FTS queries before
Expand Down
3 changes: 3 additions & 0 deletions README.org
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ openssl rand -base64 32 > secrets/couchdb_password
openssl rand -base64 48 > secrets/couchdb_secret
openssl rand -hex 24 | tr '[:lower:]' '[:upper:]' > secrets/erlang_cookie
openssl rand -base64 32 > secrets/rabbitmq_password
openssl rand -base64 32 > secrets/valkey_password
chmod 0600 secrets/*

nix run .#load-images
Expand All @@ -135,6 +136,7 @@ Default local endpoints:
| CouchDB | http://127.0.0.1:5984 |
| RabbitMQ AMQP | 127.0.0.1:5672 |
| RabbitMQ management | http://127.0.0.1:15672 |
| Valkey | 127.0.0.1:6379 |

See [[file:DOCKER.md][DOCKER.md]] for image builds, secrets, persistence, migration, backup, FTS,
and upgrades.
Expand Down Expand Up @@ -226,6 +228,7 @@ The same path can be supplied through =STAR_SERVER_INIT_FILE=.
| [[file:docs/document-spec.org][docs/document-spec.org]] | StarIntel 0.9 and legacy 0.8 documents, types, relations, IDs, metadata, and provenance |
| [[file:docs/configuration.org][docs/configuration.org]] | Environment, init files, secrets, advanced examples, and tuning |
| [[file:docs/http-api-docs.org][docs/http-api-docs.org]] | HTTP endpoint reference and examples |
| [[file:docs/lease-store-usage.org][docs/lease-store-usage.org]] | Backend-neutral target lease protocol examples and lifecycle |
| [[file:DOCKER.md][DOCKER.md]] | Nix-built container stack and operations |
| [[file:docs/testing.md][docs/testing.md]] | Unit, integration, and stack tests |

Expand Down
32 changes: 32 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,29 @@ services:
retries: 12
start_period: 20s

valkey:
image: starintel/valkey:9.0.1
hostname: valkey
restart: unless-stopped
environment:
VALKEY_PASSWORD_FILE: /run/secrets/valkey_password
secrets:
- valkey_password
volumes:
- valkey_data:/data
ports:
- "${VALKEY_BIND_ADDRESS:-127.0.0.1}:${VALKEY_PORT:-6379}:6379"
networks:
- backend
healthcheck:
test:
- CMD-SHELL
- VALKEYCLI_AUTH="$$(cat /run/secrets/valkey_password)" valkey-cli ping | grep -q PONG
interval: 10s
timeout: 5s
retries: 12
start_period: 10s

star-server:
image: starintel/server:0.1.0
hostname: star-server
Expand All @@ -98,6 +121,9 @@ services:
RABBITMQ_ADDRESS: rabbitmq
RABBITMQ_USER: ${RABBITMQ_USER:-starintel}
RABBITMQ_PASSWORD_FILE: /run/secrets/rabbitmq_password
VALKEY_HOST: valkey
VALKEY_PORT: 6379
VALKEY_PASSWORD_FILE: /run/secrets/valkey_password
HTTP_API_LISTEN_ADDRESS: 0.0.0.0
STAR_AUTH_MODE: api-key
STAR_AUTH_DATABASE: ${STAR_AUTH_DATABASE:-starintel-gserver-auth}
Expand All @@ -109,6 +135,7 @@ services:
secrets:
- couchdb_password
- rabbitmq_password
- valkey_password
- auth_pepper
- auth_bootstrap_secret
volumes:
Expand All @@ -125,6 +152,8 @@ services:
condition: service_healthy
rabbitmq:
condition: service_healthy
valkey:
condition: service_healthy
healthcheck:
test: [CMD, curl, --fail, --silent, http://localhost:5000/health]
interval: 10s
Expand All @@ -141,6 +170,8 @@ secrets:
file: ${CREDENTIALS_DIR:-./secrets}/erlang_cookie
rabbitmq_password:
file: ${CREDENTIALS_DIR:-./secrets}/rabbitmq_password
valkey_password:
file: ${CREDENTIALS_DIR:-./secrets}/valkey_password
auth_pepper:
file: ${CREDENTIALS_DIR:-./secrets}/auth_pepper
auth_bootstrap_secret:
Expand All @@ -150,6 +181,7 @@ volumes:
clouseau_index:
couchdb_data:
rabbitmq_data:
valkey_data:

networks:
backend:
41 changes: 41 additions & 0 deletions docker/valkey-entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#!/bin/sh
set -eu

password_file="${VALKEY_PASSWORD_FILE:-/run/secrets/valkey_password}"
if [ ! -r "$password_file" ]; then
echo "Valkey password file is not readable" >&2
exit 1
fi

password="$(cat "$password_file")"
if [ -z "$password" ]; then
echo "Valkey password file is empty" >&2
exit 1
fi

runtime_dir=/run/starintel-valkey
acl_file="$runtime_dir/users.acl"
mkdir -p "$runtime_dir"
password_hash="$(printf %s "$password" | sha256sum | cut -d ' ' -f 1)"
unset password
# Least-privilege service credential: deny everything by default, then grant
# only the commands the lease adapter invokes directly or from Lua scripts, and
# restrict keys to the StarIntel lease namespace owned by this deployment.
# Direct adapter commands: AUTH, PING, EVAL, SCAN, GET (list/inspect helpers).
# Lua script commands: GET, SET, DEL, INCR, PTTL, TIME.
# Administrative, dangerous, pub/sub, and unrelated-key access stays denied.
printf 'user default on #%s ~starintel:target-lease:v1:* -@all +auth +ping +eval +get +set +del +incr +pttl +time +scan\n' \
Comment thread
lost-rob0t marked this conversation as resolved.
"$password_hash" > "$acl_file"
unset password_hash
chown -R 65532:65532 "$runtime_dir"
chmod 0700 "$runtime_dir"
chmod 0600 "$acl_file"

exec setpriv --reuid=65532 --regid=65532 --clear-groups valkey-server \
--bind 0.0.0.0 \
--protected-mode yes \
--aclfile "$acl_file" \
--appendonly yes \
--appendfsync everysec \
--dir /data \
"$@"
3 changes: 3 additions & 0 deletions docs/configuration.org
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ The container entrypoint reads:

- =COUCHDB_PASSWORD_FILE= and exports =COUCHDB_PASSWORD=;
- =RABBITMQ_PASSWORD_FILE= and exports =RABBITMQ_PASSWORD=.
- =VALKEY_PASSWORD_FILE= identifies the mounted secret that the composition
root passes to the lease-store constructor; its value is never exported as
an environment variable.

It then drops to UID/GID 65532 and starts:

Expand Down
9 changes: 7 additions & 2 deletions docs/index.org
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,12 @@ the corresponding controls are implemented.
11. [[file:http-auth-threat-model.org][HTTP authentication threat model]] — protected assets, attackers, boundaries, controls, failures, and residual risks. Design contract.
12. [[file:http-auth-kv-lease-boundary.org][KV lease authentication boundary]] — target-lease assets, atomic ownership, fencing, replay controls, and the KV trust boundary. Normative design contract.
13. [[file:target-lease-semantics.org][Distributed target lease semantics]] — canonical lock identity, records, state transitions, operations, idempotency, deadlines, fencing enforcement, races, errors, audit, and recovery. Normative design contract.
14. [[file:http-principal-capability-contract.org][HTTP principal and capability contract]] — principal classes, credentials, capabilities, scopes, decisions, and route mapping. Design contract.
14. [[file:lease-store-usage.org][Lease-store protocol usage]] — Common Lisp examples for canonical identity, acquire, renew, inspect, list, release, serialization, typed outcomes, and owned shutdown.
15. [[file:http-principal-capability-contract.org][HTTP principal and capability contract]] — principal classes, credentials, capabilities, scopes, decisions, and route mapping. Design contract.

** Development workflow

- [[file:next-implementation-loop-prompt.org][Next ordered implementation-loop prompt]] — copy-ready continuation prompt for issue #31 and the transition to #32.

** Implementation status

Expand All @@ -39,7 +44,7 @@ the corresponding controls are implemented.
| HTTP API-key authentication | Active | Default-deny bearer authentication, immutable request context, lifecycle routes, exact-origin CORS, and separate credential storage |
| Fine-grained route authorization | Active | Closed capabilities and tenant/dataset/actor/target scopes enforced at HTTP and embedded service boundaries; unmapped routes deny |
| Authentication revocation cache | Active | No credential cache; committed revoke/disable is visible to the next verifier lookup |
| KV target leases | Protocol/test backend | Backend-neutral typed protocol and deterministic unit-test backend are active; no production KV adapter exists and allowed HTTP operations return =501= |
| KV target leases | Protocol/Valkey backend | Backend-neutral typed protocol, deterministic unit-test backend, and atomic production Valkey adapter are active; HTTP operations remain =501= until issue #32 wires them into execution |
| Clouseau full-text search | Active in Compose | Search is authenticated and authorization clauses constrain tenant/dataset scope before backend execution |
| URL extractor pattern | Experimental | Actor starts; complete global pattern dispatcher is not evident |
| User-finder/user-hunt actors | Present, inactive | Files are not in the ASDF component list |
Expand Down
Loading
Loading