8000 feat(mesh): another machine joins as a node with the install line and lens node by adeelahmad · Pull Request #591 · adeelahmad/lense · GitHub
[go: up one dir, main page]

Skip to content

feat(mesh): another machine joins as a node with the install line and lens node - #591

Merged
adeelahmad merged 2 commits into
mainfrom
claude/project-thread-6dqeie
Oct 18, 2026
Merged

adeelahmad merged 2 commits into
mainfrom
claude/project-thread-6dqeie

Conversation

@adeelahmad
Copy link
10000 Copy Markdown
Owner

Description

Before: an admin could make a join token, but no machine could use it. There was no agent on the other side, and no install line.

After: POST /api/v1/mesh/tokens returns an install_line. On another machine, curl … install.sh | LENS_JOIN=<url> LENS_JOIN_TOKEN=<token> sh makes it a node in ~/lens-node. It asks nothing, publishes no port and starts two containers (docker-compose.node.yml):

  • mesh runs Nebula. It is the same sidecar the primary uses.
  • node runs the new lens node agent.

The agent joins once. The token never crosses the wire and is never written to .env. After that it talks to the primary over the mesh only. Every minute it fetches its config (the blocklist), renews its certificate when a third of its life is left or its key changed, and sends its report. A removed node keeps asking, so an undo brings it back. lens node status says what the machine is on the mesh, without its credentials.

The API's host check now admits the primary's mesh address for /api/v1/mesh/node/* only, and only while the mesh is on.

Motivation and Context

This is the third step of the Nebula network Adeel asked for: workers and other nodes always talk over their own private network, set up automatically. Worker nodes running jobs and the Settings › Nodes and Network pages follow in their own PRs.

Steps to reproduce (if appropriate):

I ran it end to end in this container with real Nebula 1.11.2 and Docker:

  • The node joined as compute at 10.73.0.2 and reported over the mesh.
  • Removing it put its certificate on the blocklist and tore its tunnel down. Restoring it reconnected it, and a restart carried on as the same node.
  • install.sh: joining, a rerun without a token, a refused spent token and a missing token all behave as docs/mesh.md "On a node" says.

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change; ! in the title)

Checklist:

  • The title is a Conventional Commit.
  • My code follows the code style of this project (make hooks checks it on commit).
  • I added or updated tests (tests/api/test_mesh_node.py, test_mesh.py).
  • My change requires documentation updates, and I have updated docs/ accordingly (docs/mesh.md "On a node").
  • Users need more than the title to understand it, so I wrote a fragment: changelog.d/mesh-node.md.

Done questions (AGENTS.md)

  • Goal: in progress. This is one PR of the mesh; it merges on green.
  • Agents: the install line comes from the API (install_line); lens node status prints JSON.
  • Zero setup: one install line, which installs Docker if it's missing; nothing is asked.
  • Fewest clicks and keystrokes: copy one line, paste it.
  • Configurable: n/a, no new settings (the mesh settings are feat(mesh): Lens as the CA of a Nebula mesh between its machines, with join tokens and a node registry #546's).
  • Light: nodes use the lean image; nothing changes on installs without nodes.
  • Private and secure: threat model updated (node calls at the mesh address; two containers, nothing published).
  • Cost: n/a, no model calls.
  • User decides: the admin makes each token; defaults are unchanged.
  • Upgrades: n/a, no stored-data change.
  • Honest: docs and README label the mesh in progress.
  • Tests, docs and generated files updated (make openapi).
  • Open standards; dependencies keep Lens MIT (Nebula is MIT; no new dependencies).
  • Result reported with evidence; refine later items are in docs/mesh.md (an upgrade agent on nodes).
  • Reuse: the primary's sidecar, mesh.join and httpx.

Generated by Claude Code

… lens node

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GTrHPwWHtEvFJFq3M1YpH4
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GTrHPwWHtEvFJFq3M1YpH4
@adeelahmad
adeelahmad merged commit d14ed08 into main Oct 18, 2026
21 checks passed
@adeelahmad
adeelahmad deleted the claude/project-thread-6dqeie branch October 18, 2026 12:18
adeelahmad pushed a commit that referenced this pull request Oct 18, 2026
The mesh's install line landed (#591), so a group on a cloud, Proxmox or
SSH provider could launch, but a node runs no `lens worker` yet: its
units would never count as joined and only cost until given up on.
can_join now also needs the mesh to say its nodes run workers
(WORKER_NODES), and waiting_for says which of the three it waits for.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ALjNbLobvPe7VwvDL8j7V3
adeelahmad added a commit that referenced this pull request Oct 18, 2026
* feat(scaling): Linux hosts over SSH as a provider

Each unit is the first-boot script started in its own process group on
one of the provider's hosts, up to per_host each; letting it go stops the
group. Host keys are trusted on first use when an admin saves or checks
the provider, kept when a later check fails, and held to on every call.
Paramiko is the ssh extra, fetched when the first SSH provider is saved.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ALjNbLobvPe7VwvDL8j7V3

* fix(scaling): launch on other machines only once mesh nodes run workers

The mesh's install line landed (#591), so a group on a cloud, Proxmox or
SSH provider could launch, but a node runs no `lens worker` yet: its
units would never count as joined and only cost until given up on.
can_join now also needs the mesh to say its nodes run workers
(WORKER_NODES), and waiting_for says which of the three it waits for.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ALjNbLobvPe7VwvDL8j7V3

---------

Co-authored-by: Adeel Ahmad <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

0