Install and run OpenArch with Docker Compose, from one command line tool.
OpenArch CLI (openarch) is a command line tool to install and run OpenArch with Docker Compose.
It creates a workspace folder with plain files: compose.yaml, .env, .env.local and a few config files.
You can read them, and you can run docker compose on them without the CLI.
- Plain files. The workspace is a normal Docker Compose project. You can keep using it without the CLI.
- For people and for scripts. Every question in the interactive mode is also a flag.
- For automation and AI agents. Every command supports
--output json, and the exit codes are stable. - Secure by default. Generated passwords, Compose secrets and optional HTTPS. You can turn off each part.
- After the install. Status, logs, users, upgrades, backups, restores and password rotation.
- Signed releases. Checksums are signed with Sigstore, and
openarch self-updatechecks the signature.
- Docker with Docker Compose v2 or later
- About 4 GB of memory for Docker
- Go 1.26 or later, only to build the CLI from source
On native Linux, OpenSearch needs vm.max_map_count set to at least 262144. openarch doctor checks this.
With Homebrew (macOS and Linux):
brew install AlfrescoLabs/tap/openarch
With Scoop (Windows):
scoop bucket add alfrescolabs https://github.com/AlfrescoLabs/scoop-bucket
scoop install openarch
Homebrew also installs the shell completions. Update with brew upgrade openarch or
scoop update openarch.
Archives, signature checks and builds from source are in More install options.
openarch doctor
openarch init --dir my-openarch
openarch up --dir my-openarch
For a tour of every feature in a temporary folder, see docs/walkthrough.md.
up returns when OpenArch is healthy. OpenArch is then at http://localhost:8080, and the Swagger UI
is at http://localhost:8080/swagger-ui/index.html. The first start pulls about 2 GB of images, so it
takes a few minutes.
The admin user is admin. Its password is in my-openarch/.env.local, as OPENARCH_ADMIN_PASSWORD.
The current release is 0.5.1. The commands:
| Command | What it does |
|---|---|
openarch doctor |
Checks Docker, Docker Compose, memory, free ports and access to the image registry |
openarch init |
Creates a workspace with every deployment option |
openarch render |
Writes the workspace files again from openarch.yaml |
openarch up |
Starts the stack and waits until it is healthy |
openarch status |
Shows the state of each service and of the OpenArch API |
openarch logs |
Shows the logs of the services |
openarch down |
Stops the stack and keeps the data |
openarch api ping |
Checks that OpenArch answers and that the credentials work |
openarch config |
Saves connections to OpenArch servers (set, get, list, use, delete, whoami) |
openarch query |
Runs a query and lists the documents found |
openarch documents |
Gets, creates and lists documents, and uploads and downloads their files |
openarch users |
Manages the OpenArch users of a workspace (list, add, set-password, delete) |
openarch smoke-test |
Checks OpenArch end to end: create, read, query and delete a document |
openarch upgrade |
Changes the OpenArch version, then checks the result |
openarch migrate |
Updates openarch.yaml to the schema of this release |
openarch backup, openarch restore |
Back up and restore MongoDB, the OpenArch files and the OpenSearch indices |
openarch clean |
Stops the stack and deletes its data |
openarch secrets rotate |
Gives new passwords to the OpenArch, MongoDB and OpenSearch users |
openarch mcp |
Runs an MCP server, so that AI agents can use the commands as tools |
openarch skill |
Installs the Agent Skill that teaches AI coding agents to use the CLI (install, show) |
openarch self-update |
Replaces the binary with the latest release, after a signature check |
openarch completion |
Prints the shell completion script for bash, zsh, fish or PowerShell |
openarch version |
Prints the version |
Download the archive for your system from the
releases page: Linux, macOS and Windows, on
amd64 or arm64. Each archive has the openarch binary, the license, this README and the walkthrough.
VERSION=0.5.1
OS=linux # or darwin, windows
ARCH=amd64 # or arm64
curl -LO "https://github.com/AlfrescoLabs/openarch-cli/releases/download/v$VERSION/openarch_${VERSION}_${OS}_${ARCH}.tar.gz"
curl -LO "https://github.com/AlfrescoLabs/openarch-cli/releases/download/v$VERSION/checksums.txt"
sha256sum --ignore-missing -c checksums.txt # on macOS: shasum -a 256 --ignore-missing -c checksums.txt
tar xzf "openarch_${VERSION}_${OS}_${ARCH}.tar.gz"
sudo mv openarch /usr/local/bin/
openarch version
On Windows, download the .zip archive and put openarch.exe in a folder on your PATH.
The binaries are not signed with an Apple or Microsoft certificate. When a browser downloaded the file,
macOS may block it: run xattr -d com.apple.quarantine openarch once. Windows may show a SmartScreen
warning.
checksums.txt is signed with cosign by the release workflow of this
repository (keyless, with the GitHub Actions identity). Each archive also has an SBOM
(.sbom.json, SPDX format).
curl -LO "https://github.com/AlfrescoLabs/openarch-cli/releases/download/v$VERSION/checksums.txt.sigstore.json"
cosign verify-blob checksums.txt \
--bundle checksums.txt.sigstore.json \
--certificate-identity "https://github.com/AlfrescoLabs/openarch-cli/.github/workflows/release.yml@refs/tags/v$VERSION" \
--certificate-oidc-issuer https://token.actions.githubusercontent.com
Release 0.1.0 has checksums.txt.sig and checksums.txt.pem instead of the bundle: use
--certificate checksums.txt.pem --signature checksums.txt.sig in place of --bundle.
openarch self-update replaces the binary with the latest release. It checks the same signature and
the SHA-256 of the archive first, so it needs no cosign. openarch self-update --check only reports if
a newer release exists. Installs by Homebrew or Scoop are updated with those tools instead.
git clone https://github.com/AlfrescoLabs/openarch-cli.git
cd openarch-cli
make build
./bin/openarch version
Without make: go build -o bin/openarch ./cmd/openarch.
Completion knows the commands, the flag values, the services of the workspace (openarch logs <Tab>)
and the saved contexts (--context <Tab>, openarch config use <Tab>).
# zsh: add to ~/.zshrc (needs compinit)
source <(openarch completion zsh)
# bash: add to ~/.bashrc (needs the bash-completion package)
source <(openarch completion bash)
# fish
openarch completion fish > ~/.config/fish/completions/openarch.fish
# PowerShell: add to $PROFILE
openarch completion powershell | Out-String | Invoke-Expression
openarch completion <shell> --help shows more options.
Run openarch init in a terminal, and it asks for each option that you did not give as a flag.
At the end it prints the same command with flags, so you can repeat it or use it in a script.
With --non-interactive, or when there is no terminal, init never asks. It uses the defaults for
missing values.
| Option | Flag | Values | Default |
|---|---|---|---|
| OpenArch version | --version |
image tag of ghcr.io/hyland/openarch-app-jvm |
latest tested tag |
| Storage | --storage |
volume (Docker named volumes), bind (folders on the host), none (data is lost when the containers are removed) |
volume |
| Data folder | --data-dir |
path, with --storage bind |
./data |
| HTTPS | --tls |
none, self-signed (the CLI creates a certificate), custom (your own files) |
none |
| Certificate files | --cert, --key |
PEM files, with --tls custom |
- |
| Server name | --server-name |
host name for URLs and the certificate | localhost |
| Port | --port |
host port | 8080, or 8443 with TLS |
| Listen address | --bind-ip |
127.0.0.1 (this machine only) or 0.0.0.0 (all interfaces) |
127.0.0.1 |
| Extra ports | --expose |
mongodb (27017), opensearch (9200), debug (Java debugger, 5005), repeatable |
none |
| Extra services | --with |
dashboards (OpenSearch Dashboards on port 5601), repeatable |
none |
| Memory | --memory |
auto (from docker info) or a size such as 8g |
auto |
| Backend security | --insecure-backends |
turns off authentication in MongoDB and OpenSearch | secured |
| OpenArch users | --default-users |
keeps the users bundled in the OpenArch image | only admin |
| Admin password | --admin-password-stdin |
read from stdin | generated |
Other flags:
--dry-runlists the files thatinitwould write, and writes nothing.--forceoverwrites an existing workspace. It keeps the passwords in.env.local.--from <file>creates a workspace from an existingopenarch.yaml.
Examples:
# Data in folders on the host
openarch init --dir my-openarch --storage bind --data-dir /srv/openarch
# HTTPS with a self-signed certificate, on https://openarch.local:8443
openarch init --dir my-openarch --tls self-signed --server-name openarch.local
# HTTPS with your own certificate, open to the network
openarch init --dir my-openarch --tls custom --cert server.pem --key server-key.pem \
--server-name openarch.example.com --port 443 --bind-ip 0.0.0.0
# A machine for development: debugger, OpenSearch Dashboards, more memory
openarch init --dir my-openarch --expose debug --with dashboards --memory 8g
# Your own admin password
printf '%s' "$PASSWORD" | openarch init --dir my-openarch --admin-password-stdin --non-interactive
# Every security feature off: plain HTTP, no passwords for MongoDB and OpenSearch,
# and the bundled OpenArch users (admin:password)
openarch init --dir my-openarch --insecure-backends --default-users
Risky choices print a warning on stderr, for example --bind-ip 0.0.0.0 without TLS.
openarch.yaml is the source of truth. To change the deployment:
- Edit
openarch.yaml. - Run
openarch render --dry-runto see the diff. - Run
openarch renderto write the files. - Run
openarch up. It recreates only the services whose files or passwords changed.
render only touches files that change. When an option no longer needs a file (for example, the proxy
config after you turn off TLS), render deletes it.
After an update of openarch, a workspace made by an older release still works. Commands print a
warning, and openarch migrate updates openarch.yaml to the new schema (--dry-run shows the
diff). If a newer openarch wrote the file, run openarch self-update.
Run these in the workspace folder, or add --dir my-openarch:
openarch up # start, and wait until healthy
openarch status # state of each service and of the API
openarch logs openarch-app -f # follow the OpenArch logs
openarch down # stop, and keep the data
upruns thedoctorchecks first. Ports that this workspace already uses are not reported as busy. Use--skip-doctorto skip the checks.upwaits up to 10 minutes. Change it with--timeout, or use--wait=falseto return at once.statusexits with code 0 when the stack is healthy, and 4 in any other state (starting,unhealthy,stopped).downkeeps the named volumes and data folders. With--storage nonethere is no data to keep, so its volumes are removed.
The workspace is a plain Docker Compose project. In secure mode (the default), Docker Compose needs both env files:
cd my-openarch
docker compose --env-file .env --env-file .env.local up -d
docker compose ps
docker compose logs -f openarch-app
docker compose down
With --insecure-backends, there are no secrets, and docker compose up -d is enough.
After a change to a password or to a file in config/, add --force-recreate to up. openarch up
does this for you, and only for the services that need it.
Commands that call the API, such as openarch api ping, find the URL, user and password in this order:
- Flags:
--url,--user,--password-stdin.--passwordworks too, but it prints a warning, because the password stays in the shell history. - Environment variables:
OPENARCH_URL,OPENARCH_USER,OPENARCH_PASSWORD. - A saved connection (a context, see below) chosen with
--contextorOPENARCH_CONTEXT. - The workspace in
--diror in the current folder: the URL fromopenarch.yaml, and the admin user and password from.env.local. - The current context.
- A hidden prompt, when the command runs in a terminal.
The first source that has a value wins, for each value. The user and password of a context or a
workspace are used only with its own URL, so they are never sent to another server.
openarch config whoami shows which source gave each value. It never shows the password.
openarch api ping # inside a workspace: no flags needed
openarch api ping --dir my-openarch --output json
printf '%s' "$PASSWORD" | openarch api ping --url https://openarch.example.com --user admin --password-stdin
- With a self-signed workspace certificate, the CLI trusts the workspace certificate. It also connects to the published port on this machine, so the server name does not need to resolve.
--insecure(orOPENARCH_INSECURE=true) turns off certificate checks, with a warning each time.--verboselogs each request, with theAuthorizationheader replaced byREDACTED.- Exit code 6 means the credentials are missing or wrong, or the user has no permission (HTTP 403). Exit code 5 means the server cannot be reached or returned an error.
openarch query "SELECT * FROM SysFolder"
openarch query "SELECT * FROM SysContent" --limit 50 --sort sys_title --output json
openarch documents get / # by path: the value starts with /
openarch documents get dae8e1bb-ef08-44e5-81ae-451d785a3643 # by id
openarch documents create --parent / --type SysFolder --name projects --title Projects
openarch documents create --parent /projects --file report.json
openarch documents list /projects
openarch documents upload report.pdf --parent /projects
openarch documents content /projects/report.pdf --out report.pdf
- Queries use HxQL, a SQL-like language, by default.
--syntax exprselects the expression syntax. - New documents reach the search index after a short delay.
--syncmakes the query wait for it. - Full-text queries (
WHERE sys_fulltext = 'word') work only on the search index, so do not use--syncwith them. They find words in the name, title and description, not inside uploaded files: OpenArch stores files but does not extract their text. documents contentchecks the SHA-256 of the download against the digest that OpenArch stored.- With
--output json, documents and query results are the server responses as they are.
openarch smoke-test creates a folder under the root, reads it by id and by path, finds it with a
query and in the background search index, deletes it, and checks that it is gone. It reports each
step, and deletes the folder even when a step fails.
openarch smoke-test
openarch smoke-test --context prod --output json
Exit code 0 means every step passed. Exit code 4 means a step failed, and 6 means the credentials were
rejected. --index-timeout (default 60s) sets how long to wait for the search index.
openarch upgrade --version 1.0.93 --dry-run # show the plan
openarch upgrade --version 1.0.93 # pull, render, start, smoke test
openarch backup # into backups/<time> in the workspace
openarch restore backups/20261103T120415Z # replaces all the data
openarch clean # deletes all the data
upgradepulls the new image first, so a version that does not exist changes nothing. Then it writes the version intoopenarch.yaml, starts the stack, waits until it is healthy, and runs the smoke test. There is no automatic rollback: make a backup first.backupwritesmongodb.archive.gz(amongodump),files.tar.gz(the OpenArch files),opensearch.tar.gz(an OpenSearch snapshot of the OpenArch indices) andmanifest.json(the version, and a SHA-256 for each file). The backup folder and its files are private (mode 0700 and 0600), andbackups/is git-ignored. The backup runs while OpenArch runs: changes made during the backup may be missing from it.restorechecks every file of the backup first. Then it stops OpenArch, restores the three parts, starts the stack and waits until it is healthy. It works on a running stack and afterclean.cleandeletes the containers and volumes. With--storage bind, the data folders are kept.restoreandcleanask for confirmation, and need--yeswithout a terminal.- The passwords of MongoDB and OpenSearch never leave the containers: the backup commands read them from the container secrets.
OpenArch has no API to manage users. It reads them from config/users.yml when it starts. The
users commands change that file, and openarch up restarts OpenArch to apply the change.
openarch users list
openarch users add alice --email alice@example.com # asks for the password
openarch users add bob --admin --password-stdin < bob.txt
openarch users set-password alice
openarch users delete alice
openarch up # apply the changes
- Passwords are stored as bcrypt hashes. The passwords of added users are not stored anywhere else.
users set-password adminalso changesOPENARCH_ADMIN_PASSWORDin.env.local, so the CLI keeps working.openarch renderkeeps every user and group in the file.- These commands work on a workspace only, and not with
--default-users.
A context is a saved connection: a local workspace, or the URL and user of a remote OpenArch.
openarch config set --name prod -s https://openarch.example.com -u admin # asks for the password
openarch config list
openarch config use prod
openarch api ping # uses the current context
openarch api ping --context local # or any context, from any folder
openarch config get --name prod # the password is masked
openarch config delete prod # also removes the stored password
openarch initsaves each workspace as a context:localfor the first one, then the workspace name. Use--context-nameto choose the name.config setchecks the connection before it saves it.--no-verifyskips the check. The password comes from a hidden prompt or--password-stdin, never from a command argument.- Contexts are in
config.yaml, in~/.config/openarch(or$XDG_CONFIG_HOME/openarch, or%AppData%\openarchon Windows, orOPENARCH_CONFIG_DIR). That file never holds a password. - Passwords are in the OS credential store: Keychain on macOS, Credential Manager on Windows, and Secret
Service on Linux. When the OS store is not available (for example, on a Linux server with no
desktop), they are in
credentials.json(mode 0600) in the same folder, with a warning.OPENARCH_CREDENTIAL_STORE=keychainorfileforces a backend. config get --show-secretsshows the passwords.- Without a terminal,
config deleteneeds--yes.
| File | Content |
|---|---|
openarch.yaml |
All options. Edit this file, then run openarch render. |
compose.yaml |
The services. Generated. |
.env |
Versions, ports and memory settings. No secrets. Generated. |
.env.local |
All passwords. Mode 0600, git-ignored. |
config/users.yml |
OpenArch users, with bcrypt hashes only. |
config/opensearch/ |
OpenSearch security config, with bcrypt hashes only. |
config/Caddyfile, certs/ |
HTTPS proxy config and the self-signed certificate. |
README.md |
Endpoints, users and commands of this workspace. |
flowchart LR
client[Client] -->|HTTP 8080| app[openarch-app]
client -.->|HTTPS 8443, with --tls| proxy[proxy: Caddy]
proxy -.-> app
app --> mongodb[(mongodb)]
app --> opensearch[(opensearch)]
init[mongodb-init] -->|replica set and users| mongodb
dashboards[opensearch-dashboards] -.->|with --with dashboards| opensearch
openarch-app: the OpenArch API. With TLS, it is not published on the host. Only the proxy is.mongodb: a one-node replica set, which OpenArch needs.mongodb-init: a one-shot service. It starts the replica set and creates the OpenArch database user.opensearch: the search index.proxy: only with--tls.opensearch-dashboards: only with--with dashboards.data-init: only with--storage bind. It sets the owners of the data folders.
By default, only the OpenArch port (or the proxy port) is published. MongoDB and OpenSearch stay on the
internal Docker network unless you use --expose.
Security is on by default. Every part can be turned off.
| Part | Default | To turn it off |
|---|---|---|
| OpenArch users | Only admin, with a generated password. The users bundled in the image are not loaded. Add more with openarch users add. |
--default-users |
| MongoDB | Authentication on. Users root and openarch (read and write on its own database only). |
--insecure-backends |
| OpenSearch | Security plugin on. Users admin, openarch (cannot change security settings) and kibanaserver. |
--insecure-backends |
| OpenSearch Dashboards | Login required. | --insecure-backends |
| HTTPS | Off. | Turn it on with --tls self-signed or --tls custom. |
How passwords are handled:
- The CLI generates every password and stores it only in
.env.local. - Docker Compose reads
.env.localand gives each password to its container as a file in/run/secrets. Passwords are not incompose.yaml, not in container environment variables, and not indocker inspect. - Config files hold only bcrypt hashes.
- The CLI never prints a password.
Notes:
- OpenArch always needs a user and password for its API. Only the health check, the Swagger UI and the API docs are public.
- Inside the Docker network, OpenSearch uses plain HTTP, with users and passwords required.
openarch secrets rotategives new random passwords to the OpenArch admin, the MongoDB users and the OpenSearch users. It changes them in the running services, saves them in.env.local, and recreates the services that use them.--onlyselects some of them, and--dry-runshows the plan.- Do not edit the MongoDB root or OpenSearch passwords in
.env.localby hand: the services keep the old ones. Useopenarch secrets rotate. The OpenArch admin password and the MongoDBopenarchpassword can be edited by hand, followed byopenarch renderandopenarch up.
Every command, with its flags and examples: docs/commands/openarch.md.
Use --output json (or OPENARCH_OUTPUT=json) to get one JSON document on stdout. Errors are JSON
too in this mode. Logs and warnings always go to stderr.
openarch help --output json lists every command with its flags, default values, allowed values and
examples, plus the exit codes. openarch <command> --help --output json describes one command.
docs/llms.txt gives the same overview to AI agents in the llms.txt format.
| Code | Meaning |
|---|---|
| 0 | OK |
| 1 | Error |
| 2 | Bad usage or input |
| 3 | A prerequisite is missing (for example, doctor found a problem) |
| 4 | A service is not healthy |
| 5 | Remote API error |
| 6 | Authentication failed, or no permission |
openarch mcp runs a Model Context Protocol server on stdio, so that an
AI agent can install and operate OpenArch with the same commands. Each tool runs one command with
--output json and returns its result. A failed command gives an error result that starts with the exit
code.
# Claude Code
claude mcp add openarch -- openarch mcp --dir /path/to/my-openarch
# Any MCP client: the command and its arguments
openarch mcp --dir /path/to/my-openarch
--diror--contextpins the server to one workspace or connection. The default is the current folder.- Tools that stop services or delete data (
down,clean,restore,secrets_rotate,users_delete,config_delete) exist only with--allow-destructive. - The
inittool always keeps the secure defaults. Its options that can turn security off (default-users,insecure-backends,bind-ip,expose,tls,cert,key,from) exist only with--allow-insecure. - Tools never take a password and never return one. Commands that need a password as input
(
config set,users add,users set-password) are not tools. - The tool parameters are the flags of each command. Progress goes to MCP progress notifications, and logs go to stderr.
- examples/docker-agent/install installs OpenArch with a local agent and a local model (Docker Agent and Docker Model Runner).
- A result longer than
--max-result-bytes(default 16384, about 4000 tokens) is cut, with a note that names the parameters for a smaller result. Use0for no limit.
openarch skill install writes an Agent Skill to
.claude/skills/openarch/SKILL.md. Claude Code and other coding agents that read skills then know how
to install and operate OpenArch with the CLI: the install flow, the JSON output, the exit codes and the
fixes for common failures. The skill is part of the binary, so it matches the commands of the release.
# For one project: run it in the project folder
openarch skill install
# For every project
openarch skill install --dir ~
# Print it, for agents that read skills from another folder
openarch skill show
Then ask the agent, for example: "Set up OpenArch for local development in ./openarch".
Run openarch skill install again after an update to get the skill of the new release. A SKILL.md
that you changed is replaced only with --force.
examples/docker-agent/skill installs OpenArch with a local agent that has only a shell and the skill, and lists the local models that work.
For an agent that does not read skills, add these lines to its instructions file:
OpenArch runs locally with the openarch CLI. Use --output json --non-interactive on every command.
Start with 'openarch doctor', then 'openarch init --dir openarch', then 'openarch up --dir openarch'.
Run 'openarch help --output json' for every command and exit code. Never put a password in an argument.
go test ./...
go test ./internal/templates -update # rewrite the golden files after a template change
make generate # regenerate the API client from api/openapi.json
make docs # rewrite docs/commands after a command or flag change
go run github.com/golangci/golangci-lint/v2/cmd/golangci-lint@latest run ./...
- CLI tests are
testscriptfiles intestdata/script/. test/e2e.sh bin/openarch [init flags]runs the real stack with Docker: init, up, smoke test, restart, clean. CI runs it on Linux in 4 modes.- The templates have golden files in
internal/templates/testdata/. - When Docker is installed, the tests also check each golden case with
docker compose config. - The API client in
internal/api/genis generated withoapi-codegenfromapi/openapi.json, the spec of a running OpenArch (/v3/api-docs). CI fails when the generated code is not up to date. - The command reference in
docs/commandsis built from the cobra commands. A test fails when it is not up to date.
- Push a tag
vX.Y.Z. The Release workflow builds a draft release, signschecksums.txt, checks the signature with theself-updatecode, and keeps the Homebrew cask and the Scoop manifest as the run artifactpackages. - Review the draft and publish it on GitHub.
- Run
scripts/publish-packages.sh vX.Y.Zto push the cask and the manifest to homebrew-tap and scoop-bucket.
Apache License 2.0. See LICENSE.
