Add community node protocol spec, Agent Skills, and make the failing tests hermetic
DevPlace CI / test (push) Failing after 31m42s

Community nodes (Phase 1 of plan.md): the protocol specification in
services/nodes/spec.py as the single source of truth, GET /nodes/spec.json,
a generated protocol docs page, the Community nodes API docs group, and the
implementation plan plus the residential proxy research. The no-open-proxy rule
is enforced in the spec (open_proxy cannot be set).

Agent Skills: register the Skills API docs group, cascade skills on account
deletion, add the nested CLAUDE.md, README and routes docs, and cover the
parser, the fetcher (end to end against a mock origin), the data layer and the
routes with 73 unit and 16 api tests.

Tests made hermetic with the repo's existing patterns instead of ambient
environment: embeddings tests set OPENROUTER_API_KEY via monkeypatch, the
workspace tests install FakeBackend via runtime.set_backend, and the skills
install tests use run_async. DockerCliBackend now returns 127 (command not
found) when the CLI is missing instead of raising FileNotFoundError, so a host
without docker answers with a clean error rather than a 500. The Providers tab
empty state no longer names the Model routes tab.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-14 07:13:25 +02:00
co-authored by Claude Sonnet 5.5
parent cae139ead9
commit f55c7ee5f4
102 changed files with 7414 additions and 65 deletions
+69
View File
@@ -0,0 +1,69 @@
# retoor <retoor@molodetz.nl>
from .._shared import endpoint
GROUP = {
"slug": "nodes",
"title": "Community nodes",
"intro": """
# Community nodes
Members can donate a little of their internet connection to the DevPlace community. A donated
**node** is a small program on the member's own machine that dials out to DevPlace and, when
asked, fetches a public page from a short list of allowed websites on DevPlace's behalf. This is
how search reaches sites that block data-centre addresses.
- **Opt-in only.** Nobody is enrolled by default. Donating needs an explicit consent that only
the account holder can grant or withdraw.
- **Not an open proxy.** A node refuses everything that is not on the published allowlist, every
port except 80 and 443, and every private or local address. The refusal is enforced on the
donor's machine as well as on the server.
- **Counted and visible.** Requests and bytes relayed are counted by the server and shown to the
donor.
The wire protocol, every limit and the allowlist are published as one machine-readable document
at `/nodes/spec.json` and rendered on the [Node network protocol](/docs/nodes-protocol.html)
page. Every endpoint follows the shared [Conventions & Errors](/docs/conventions.html).
""",
"endpoints": [
endpoint(
id="nodes-spec",
method="GET",
path="/nodes/spec.json",
title="Node protocol specification",
summary="The single source of truth for the node protocol: version, frames, limits, allowlist and refusal codes.",
auth="public",
interactive=True,
sample_response={
"version": 1,
"min_supported": 1,
"open_proxy": False,
"allowed_ports": [80, 443],
"limits": {"max_devices_per_user": 5, "max_connected_nodes": 2000},
"frames": [
{
"type": "hello",
"direction": "c2s",
"max_bytes": 262144,
"fields": [{"name": "protocol", "type": "int", "required": True}],
}
],
"destinations": [
{
"suffix": "reddit.com",
"adapter": "reddit",
"methods": ["GET"],
"ports": [80, 443],
"modes": ["tunnel", "fetch"],
"max_rps": 0.5,
}
],
"refusals": [{"code": "host_not_allowed", "meaning": "destination is not on the allowlist"}],
},
notes=[
"`open_proxy` is always `false`. A change to that would be a protocol change, not a setting.",
"Clients and the server read the same numbers from here; a contract test fails when they drift.",
],
),
],
}