feat: serve the BitMind gateway from the server process - #3
Merged
Conversation
Standalone mode removed the reason the gateway ran on its own: the server refused to boot without the Intelligence contract, and the enclave has none. It now boots with the server, so the enclave supervises one process and one signal stops both, and src/bitmind/main.ts retires. On a listener of its own, not a path on the server's port. The server port is what an operator publishes; putting a service-token-authenticated relay on it would export the enclave's private surface wherever that port goes without anybody choosing it. Loopback stays the default. Setting none of the BITMIND_* variables mounts no gateway; setting any of them requires the rest, so a half-configured enclave fails at boot rather than answering BitMind with a server that cannot relay.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Next slice of the enclave work (bit-mind CopilotKit#20): the BitMind gateway now boots with the OpenBot server, and
server/src/bitmind/main.tsretires.Why now
The gateway was a separate process for one reason, stated in
main.ts: the server refused to start without the Intelligence contract, and the enclave has none. PR #2 removed that reason. Two processes with two configurations, two supervision units and two shutdown paths is a cost the enclave was paying for a constraint that no longer exists.What changed
server/src/bitmind/mount.ts—bitmindGatewayFrom(env)returns a gateway or nothing, andserveBitmindGateway(mount)binds it.server/src/index.tsmounts it after the server's ownserve()and stops it onSIGINT/SIGTERMalongside the listeners.server/src/bitmind/main.tsand thebitmind:dev/bitmind:startscripts are gone.docs/bitmind-gateway.mddocuments the variable table and the port decision.Two decisions worth reviewing
A listener of its own, not a path on the server's port. The gateway keeps
BITMIND_GATEWAY_HOST/PORT(loopback,4310) rather than answering under the server's port. The server port is the published one — an ingress, a compose mapping — and adding/bitmind/v1to it would export a service-token-authenticated relay wherever that port goes, without anyone choosing it. bit-mind'sopenbot-single-host-enclave.mdrequires the gateway be reachable only on loopback or an approved private path; this keeps that property a configuration fact rather than an operator's responsibility to notice. The subprocess boot test asserts both halves: the gateway answers on its own port, and the server's port returns404for/bitmind/v1/attestationwith the service token in hand.All or nothing on configuration. Setting no
BITMIND_*variable mounts nothing — an ordinary deployment is unaffected and needs no opinion. Setting any of them validates all of them, soBITMIND_GATEWAY_PORTalone, or a service token with no agent token, is a boot failure naming the missing variable. The alternative — boot, then refuse every relay — makes a deployment mistake look like BitMind's fault.The mount is not gated on
OPENBOT_RUNTIME_MODE=standalone: the gateway relays to a downstream AG-UI agent, which is orthogonal to whether this server also serves chat. The mode gates chat, threads and routines; it has no bearing on whether this deployment is the one BitMind talks to. Say if you would rather it refuse outside standalone.Verification
Local gate on
519a875(Actions still blocked account-wide by org billing):New tests:
server/tests/bitmind-mount.test.ts(10) pins the gate — nothing mounted from a bare environment, each half-configured shape a boot failure, the listen defaults, a malformed port refused, and the mounted gateway is the real one (401 without the token, an honestisolated_computers: falsewith it).server/tests/standalone-boot.integration.test.tsgains a second real-process boot for the claims only a boot can settle.One thing I got wrong and want to name: my first edit inserted the mount block twice — the stray copy landed inside
resolveRequestActor, which would have started a new listener on every authenticated CopilotKit request. Lint caught it as an unused binding; the tests did not, because standalone has no path through that function. Fixed before the commit, and the reason the gate is run before every push rather than after.Not in this slice
BITMIND_*variables. The enclave runs the server directly per bit-mind's enclave note, not through the chart, and chart values need their own CI target and key check. Worth doing if the gateway ever ships in a charted deployment.workflowscope:standalonein the chart job'smatrix.targetin.github/workflows/ci.yml.Refs bit-mind#20. Next fork slice: tool descriptors and interrupt emission, so bit-mind's approval gate has a real producer.