feat: issue an enrolment token from the command line #8

Merged
day01 merged %!d() commits from %!s() into %!s() 2026-08-07 08:35:52 +00:00
Owner

A node cannot join a control plane it has no token for, and there was no way to get one. issue_token was reachable from code and tests and from nothing an operator can run, which made the node protocol unreachable in practice.

The token is stored before it is printed, so one that reaches an operator is one the control plane will honour. Scope is stated the way an operator thinks of it — instance, organisation:<name>, repository:<owner>/<name> — and parsing that shape lives in the binary rather than the domain, because it is a convenience of the command line and not something the control plane means.

What this unblocked

The first end-to-end run against a live control plane, with a real runner over gRPC:

$ syncode-control issue-token --scope instance
b1fb58a4-…
$ syncode-runner register --config runner.toml --state state.json --token-file token.txt
Registered runner e2e-node
$ curl -X POST -H 'x-gitea-event: push' … /events
queued 1 run(s)

And in the database afterwards:

 sequence |                       event
----------+---------------------------------------------------
        1 | "Queued"
        2 | {"Assigned": {"node": "6cfd0246-…", "fence": 1, …}}
        3 | {"Started":  {"node": "6cfd0246-…", "fence": 1, …}}

The node enrolled over gRPC, opened a session, declared its capabilities and capacity (Active, 10 cores), was handed the plan the push compiled to, and reported that it had started — all under a lease with a fence, all persisted.

Execution stops there in this setup because the forge was a stub with no git repository behind it. Fetching sources needs a real forge, which is the deployment step.

45 tests, fmt, clippy -D warnings, both structural checks.

A node cannot join a control plane it has no token for, and there was no way to get one. `issue_token` was reachable from code and tests and from nothing an operator can run, which made the node protocol unreachable in practice. The token is stored before it is printed, so one that reaches an operator is one the control plane will honour. Scope is stated the way an operator thinks of it — `instance`, `organisation:<name>`, `repository:<owner>/<name>` — and parsing that shape lives in the binary rather than the domain, because it is a convenience of the command line and not something the control plane means. ## What this unblocked The first end-to-end run against a live control plane, with a real runner over gRPC: ``` $ syncode-control issue-token --scope instance b1fb58a4-… $ syncode-runner register --config runner.toml --state state.json --token-file token.txt Registered runner e2e-node $ curl -X POST -H 'x-gitea-event: push' … /events queued 1 run(s) ``` And in the database afterwards: ``` sequence | event ----------+--------------------------------------------------- 1 | "Queued" 2 | {"Assigned": {"node": "6cfd0246-…", "fence": 1, …}} 3 | {"Started": {"node": "6cfd0246-…", "fence": 1, …}} ``` The node enrolled over gRPC, opened a session, declared its capabilities and capacity (`Active`, 10 cores), was handed the plan the push compiled to, and reported that it had started — all under a lease with a fence, all persisted. Execution stops there in this setup because the forge was a stub with no git repository behind it. Fetching sources needs a real forge, which is the deployment step. 45 tests, `fmt`, `clippy -D warnings`, both structural checks.
day01 added 1 commit 2026-08-07 08:29:40 +00:00
		A node cannot join a control plane it has no token for, and there was no way to
get one: issue_token was reachable from code and from tests, and from nothing an
operator can run. The control plane was therefore impossible to enrol against,
which made the whole node protocol unreachable in practice.

The token is stored before it is printed, so one that reaches an operator is one
the control plane will honour. The scope is stated as an operator thinks of it —
instance, organisation:<name>, repository:<owner>/<name> — and parsing that
shape lives in the binary rather than the domain, because it is a convenience of
the command line and not something the control plane means.
	
day01 merged commit 5680b022bf into %!s() 2026-08-07 08:35:52 +00:00
day01 deleted branch feat/issue-an-enrolment-token-from-the-command-line 2026-08-07 08:35:52 +00:00
Sign in to join this conversation.
No Reviewers
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: syncode/control#8