Skip to content

Commit 5b0da47

Browse files
authored
Merge pull request #406 from MiniMax-AI/stop-publishing-core-admin-port
Stop publishing Core's admin port on the host
2 parents a2946a7 + 2be907d commit 5b0da47

17 files changed

Lines changed: 61 additions & 82 deletions

File tree

‎AGENTS.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ OpenAgentCore is protocol-first and modular. Core orchestrates operations that p
88

99
OpenAgentCore is infrastructure. Change a boundary only when the existing protocol cannot express the behavior, and make that the smallest change that leaves the design intact. Hold the code to the standard of a careful, widely used open-source service.
1010

11+
Keep it concise. Write elegant code that reuses existing code and standard SDKs as far as possible, and avoid redundant code. Expose nothing that does not need to be exposed: no port, route, command or setting without a caller.
12+
1113
### Protocols at every boundary
1214

1315
- Each boundary between components has exactly one protocol: one code file (interface, wire types and validators) and one document. A protocol change edits both and every implementation in one change, reviewed on its own.

‎deploy/compose/ports.yaml‎

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
1-
# Host installation publishes Web and Core's loopback admin API for scripts on
2-
# the host. Hosting platforms omit this file and route to web:8080 themselves.
1+
# Host installation publishes Web. Hosting platforms omit this file and route
2+
# to web:8080 themselves.
33
services:
44
web:
55
ports:
66
- "${OAC_HOST:-127.0.0.1}:${OAC_WEB_PORT:-8080}:8080"
7-
core:
8-
ports:
9-
- "127.0.0.1:8091:8091"

‎deploy/compose/test_compose.py‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,14 +83,14 @@ def test_public_url_can_be_configured_after_initial_startup(self):
8383
{service: [item.get('target') for item in spec.get('volumes', [])]
8484
for service, spec in self.compose['services'].items()})
8585

86-
def test_host_ports_publish_web_and_loopback_core(self):
86+
def test_host_ports_publish_only_web(self):
8787
env = dict(os.environ, OAC_DATA_DIR='/tmp/oac-compose-fixture', OAC_HOST='0.0.0.0')
8888
hosted = json.loads(subprocess.check_output(
8989
['docker', 'compose', '--env-file', os.devnull, '-f', str(self.compose_file),
9090
'-f', str(ROOT / 'deploy/compose/ports.yaml'), 'config', '--format', 'json'], env=env))
9191
published = {name: [(port.get('host_ip'), port['published']) for port in service.get('ports', [])]
9292
for name, service in hosted['services'].items() if service.get('ports')}
93-
self.assertEqual(published, {'web': [('0.0.0.0', '8080')], 'core': [('127.0.0.1', '8091')]})
93+
self.assertEqual(published, {'web': [('0.0.0.0', '8080')]})
9494

9595
def test_platform_network_injection_keeps_the_file_valid(self):
9696
# Dokploy isolated deployments attach a project network to every service.

‎deploy/install.dev.sh‎

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -103,11 +103,8 @@ pins = json.loads((root / "deploy/compose/smoke-pins.json").read_text())
103103
"RELEASE_BASE": pins["release_base"],
104104
"ARCHIVE_CHECKSUM": pins["archive_checksum"],
105105
}))
106-
# The release ports.yaml also publishes Core on 127.0.0.1:8091. A local trial
107-
# reaches Core through Web, so only Web is published.
108-
(dest / "ports.yaml").write_text(
109-
"services:\n web:\n ports:\n - \"${OAC_HOST:-127.0.0.1}:${OAC_WEB_PORT:-8080}:8080\"\n")
110106
PY
107+
cp "$repo_root/deploy/compose/ports.yaml" "$install_dir/ports.yaml"
111108

112109
umask 077
113110
cat >"$install_dir/.env" <<EOF

‎deploy/install.sh‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,12 @@ fi
7373

7474
cleanup() {
7575
if [[ "$kept" != 1 && -d "$install_dir" ]]; then
76-
(cd "$install_dir" && docker compose down --remove-orphans) >/dev/null 2>&1 || true
76+
(
77+
cd "$install_dir"
78+
docker compose down --remove-orphans
79+
# Containers own data/; remove it from a container as well.
80+
if [[ -d data ]]; then docker compose run --rm --no-deps --entrypoint find init /data -mindepth 1 -delete; fi
81+
) >/dev/null 2>&1 || true
7782
rm -rf "$install_dir"
7883
fi
7984
}
@@ -104,7 +109,6 @@ umask 077
104109
docker compose pull
105110
docker compose create core
106111
docker compose cp core:/usr/local/bin/oac ./oac
107-
chmod 755 ./oac
108112
docker compose up -d --wait
109113
)
110114
kept=1

‎docs/api/index.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ Core serves three namespaces. Each has one kind of caller and its own credential
1212

1313
A credential used in another namespace gets 401: a Project API key on `/core/v1` or `/api/v1`, the Core key on `/v1` or `/api/v1`. How Projects and keys behave is in [Projects own assets](../concepts.md#projects-own-assets).
1414

15-
**Routing.** Web forwards `/v1`, `/api/v1` and `/docs` to Core unchanged ([console server](../web/console-server.md)). A signed-in browser reaches `/core/v1` through Web, which adds the Core key. Operator scripts call `/core/v1` on `127.0.0.1:8091` ([script the Core API](../getting-started/operations.md#script-the-core-api)).
15+
**Routing.** Web forwards `/v1`, `/api/v1` and `/docs` to Core unchanged ([console server](../web/console-server.md)). A signed-in browser reaches `/core/v1` through Web, which adds the Core key. Operator scripts call `/core/v1` inside Core's network namespace on the Core host ([script the Core API](../getting-started/operations.md#script-the-core-api)).
1616

1717
**API reference.** Core serves a read-only Swagger UI of the three namespaces at `/docs`, and the documents at `/docs/openapi.yaml`, `/docs/core.openapi.yaml` and `/docs/runtime.openapi.yaml`. No credential is required, and the page sends no API requests. Open it on the console origin, for example `http://localhost:8080/docs`. The browser loads Swagger UI from `unpkg.com`.
1818

‎docs/configuration.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ To change it, point the reverse proxy at the new address first, then edit `OAC_P
4646
| `OAC_PUBLIC_URL` | `http://localhost:8080` | Origin applications, nodes, sandboxes and self-hosted executors use. Managed domain setup writes the HTTPS origin and recreates Core and Web |
4747
| `OAC_HOST` | `127.0.0.1` | Address published by `ports.yaml`. `install.sh` sets `0.0.0.0` |
4848
| `OAC_WEB_PORT` | `8080` | Host port of Web |
49-
| `COMPOSE_FILE` | `compose.yaml:ports.yaml` | The Compose files. `ports.yaml` publishes Web and Core's loopback admin API; hosting platforms omit it |
49+
| `COMPOSE_FILE` | `compose.yaml:ports.yaml` | The Compose files. `ports.yaml` publishes Web; hosting platforms omit it |
5050
| `OAC_LOG_LEVEL` | `info` | `debug`, `info`, `warn` or `error` |
5151
| `OAC_LOG_FORMAT` | `auto` | `auto`, `text` or `json` |
5252
| `OAC_LOG_ADD_SOURCE` | unset | `1` adds source locations |
@@ -135,7 +135,7 @@ The installer creates the installation directory, `~/.oac/core` by default, with
135135
| `data/state/` | Private Provider state, including E2B receipts | Core |
136136
| `.oac.lock` | The installation lock | Mutating `oac` commands |
137137

138-
The Compose project is named `oac-<10 hex digits>`. Its services are `init`, `database`, `core` and `web`. Core applies database migrations when it starts. `web` serves the console and forwards `/v1` and `/api/v1` to Core, and it is the only service that publishes `OAC_WEB_PORT`. Host installs also publish Core's admin API on `127.0.0.1:8091`. No service receives a Docker socket. Apart from Docker's storage, nothing is written outside the installation directory.
138+
The Compose project is named `oac-<10 hex digits>`. Its services are `init`, `database`, `core` and `web`. Core applies database migrations when it starts. `web` serves the console and forwards `/v1` and `/api/v1` to Core, and it is the only service with a published port, `OAC_WEB_PORT`. No service receives a Docker socket. Apart from Docker's storage, nothing is written outside the installation directory.
139139

140140
## Appendix: Core environment without the installer
141141

‎docs/getting-started/install-options.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ The installer saves no sandbox backend. After signing in, open **System** → **
7070

7171
## Listeners and access
7272

73-
The default installation publishes Web on `--web-port` (8080) at `--host 0.0.0.0`. Core's admin API stays on `127.0.0.1:8091`. PostgreSQL stays private. `--host` is an IPv4 or IPv6 address, without a port, scheme or zone. Use a concrete server IP in the browser, not a wildcard.
73+
The default installation publishes Web on `--web-port` (8080) at `--host 0.0.0.0`. Core and PostgreSQL stay private. `--host` is an IPv4 or IPv6 address, without a port, scheme or zone. Use a concrete server IP in the browser, not a wildcard.
7474

7575
`--public-url` sets `OAC_PUBLIC_URL`, the origin applications, nodes and executors use. Set it to the HTTPS origin your reverse proxy serves.
7676

‎docs/getting-started/install.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ This page follows the default path. Every flag, existing reverse proxies and off
1919
- Linux amd64 and curl.
2020
- Docker Engine with Docker Compose 2.26.0 or newer (`docker compose version`).
2121
- An account that can run `docker` and write to its home directory. Ordinary users and root both work; the installer never calls sudo.
22-
- Free port 8080 for Web. Core's admin API uses `127.0.0.1:8091`. See [ports](./install-options.md#ports). Docker must be able to publish them; the installer does not change host policy.
22+
- Free port 8080 for Web. See [ports](./install-options.md#ports). Docker must be able to publish it; the installer does not change host policy.
2323
- For anything off this machine, the origin in `OAC_PUBLIC_URL` must be the address browsers, nodes and executors use. You can sign in on this machine first.
2424

2525
The Core host needs no KVM; nodes that run microsandbox do.
@@ -40,7 +40,7 @@ The script downloads that release's Compose files, checks their SHA-256, and:
4040

4141
1. checks Linux amd64, Docker Compose 2.26 or newer, and that the ports it will publish are free;
4242
2. creates the [installation directory](../configuration.md#installation-directory), `~/.oac/core`, writes `.env`, and copies the `oac` command out of the Core image;
43-
3. starts the services with Docker Compose. Web serves the console on port 8080 and forwards `/v1`, `/api/v1` and `/docs` to Core. Core's admin API stays on `127.0.0.1:8091`. PostgreSQL is not published.
43+
3. starts the services with Docker Compose. Web serves the console on port 8080 and forwards `/v1`, `/api/v1` and `/docs` to Core. Core and PostgreSQL are not published.
4444

4545
It saves no sandbox backend, adds no node, creates no Project or key and makes no model request. It ends by printing the console address and how to read the Core key.
4646

‎docs/getting-started/operations.md‎

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ docker compose -f ~/.oac/core/compose.yaml ps
2020
| `oac apply` | Runs `oac-core check-config`, then `docker compose up -d --wait`. A failed check changes no service |
2121
| `oac core-key [--show]` | Prints the Core key path, or the key itself with `--show` |
2222
| `oac rotate-core-key` | Replaces the Core key and restarts Core and Web |
23-
| `docker compose down --rmi all` | Removes the containers and images. Delete the installation directory afterwards |
23+
| `docker compose down` | Removes the containers. Data is kept; to delete it, [uninstall](#uninstall) |
2424

2525
For a second installation, use its directory, such as `~/.oac/second`.
2626

@@ -70,14 +70,15 @@ Keep it private. Web reads `data/secrets/web/core.key`. Core reads only its SHA-
7070

7171
### Script the Core API
7272

73-
Run scripts on the Core host against Core's loopback port. This helper reads the key from its file, keeping it off the command line:
73+
Core publishes no host port. On the Core host, this helper runs `curl` in Core's network namespace and passes the key on stdin, keeping it off the command line:
7474

7575
```sh
76-
core() { # core METHOD PATH [JSON body]
77-
curl -fsS -X "$1" "http://127.0.0.1:8091/core/v1$2" \
78-
-H @<(printf 'Authorization: Bearer %s\n' "$(~/.oac/core/oac core-key --show)") \
79-
-H 'Content-Type: application/json' ${3:+-d "$3"}
80-
}
76+
core() ( # core METHOD PATH [JSON body]
77+
cd ~/.oac/core
78+
./oac core-key --show | sed 's/^/Authorization: Bearer /' |
79+
docker run -i --rm --network "container:$(docker compose ps -q core)" curlimages/curl \
80+
-fsS -X "$1" "http://127.0.0.1:8091/core/v1$2" -H @- -H 'Content-Type: application/json' ${3:+-d "$3"}
81+
)
8182
```
8283

8384
| Task | Command |
@@ -136,11 +137,14 @@ Never prune Docker volumes or delete native harness history to make a retry pass
136137
## Uninstall
137138

138139
```sh
139-
docker compose -f ~/.oac/core/compose.yaml down --rmi all --remove-orphans
140-
rm -rf ~/.oac/core
140+
cd ~/.oac/core
141+
docker compose down --remove-orphans
142+
docker compose run --rm --no-deps --entrypoint find init /data -mindepth 1 -delete
143+
docker compose down --rmi all
144+
cd && rm -rf ~/.oac/core
141145
```
142146

143-
`down` removes the containers and images. `rm` removes the installation directory. Do the first only when you mean to delete the data.
147+
The containers own `data/`, so the `init` image deletes its contents; then `down --rmi all` removes the images and `rm` removes the installation directory. Run these only when you mean to delete the data.
144148

145149
All data goes with it: Projects and API keys, Session history, stored credentials and the Core key. To keep the data, stop the installation with `docker compose stop` instead, or [back it up](#back-up) first.
146150

@@ -183,7 +187,7 @@ Mutating `oac` commands hold `.oac.lock`. If another command holds it, retry aft
183187
| Listener | Host installation | Behind a reverse proxy |
184188
| --- | --- | --- |
185189
| Web and the API | Web publishes `OAC_WEB_PORT` (8080) on `OAC_HOST` | Web publishes `OAC_WEB_PORT` on `OAC_HOST`. Your proxy should use `127.0.0.1` |
186-
| Core admin API | `127.0.0.1:8091`. Web forwards `/v1`, `/api/v1` and `/docs` | `127.0.0.1:8091`. Web forwards `/v1`, `/api/v1` and `/docs` |
190+
| Core | No published port. Web forwards `/v1`, `/api/v1` and `/docs` | No published port. Web forwards `/v1`, `/api/v1` and `/docs` |
187191
| PostgreSQL | No published port | No published port |
188192

189193
Web signs administrators in with the Core key, checks the origin of every request, and forwards signed-in `/core/v1` requests to Core with the Core key, which stays on the server. It forwards `/v1` and `/api/v1` to Core unchanged, with the caller's own credential, serves only the non-secret node payload at `/node-install/`, and has no Docker or KVM access. Machine routes under `/api/v1` use their own enrollment and connection credentials. No service receives a Docker socket.

0 commit comments

Comments
 (0)