Reword comments and docs that still named the removed gRPC stack, rename
the GRPCSortable test interface, and drop the dead gRPC port note from the
dev README.
The per-resource tests already assert the DeleteNode/DeleteUser/RenameUser/
ExpireApiKey/DeleteApiKey 404s; keep only the cases unique to the error
file. TestAPIv1_Health now uses apiClient like the rest.
The RespLog global was read only by the removed gRPC server's logging
interceptor; HTTP logging goes through the chi zerologRequestLogger. Drop
the dead toggle and the grpc-zerolog dependency.
Asserts not-found nodes/users/keys return 404 and invalid CIDR/policy
return 400. These caught the node-500 and pre-auth-key silent-success
bugs fixed alongside.
RenameNode and ExpireNode surface ErrNodeNotInNodeStore for an unknown
node; mapStateError only knew ErrNodeNotFound, so they 500'd. Treat both
as not found.
Both updated by id without checking RowsAffected, so expiring or deleting
an unknown key silently succeeded. Return ErrPreAuthKeyNotFound, matching
DestroyPreAuthKey's documented contract.
ogen's SecurityError message echoes the operation name and internal
security text. Map it to a clean 401 so the unauthorized body stays small
and leaks nothing.
Unit-test formatOutput across json/json-line/yaml for every printed API
type and exercise the node table renderers, whose manual key/IP decode
had no coverage. These paths only ran in -o json before.
Generated API types have a pointer-receiver MarshalJSON that omits unset
optional fields. The CLI marshalled them by value, so stdlib reflection
called Opt*.MarshalJSON directly and failed with "unexpected end of JSON
input", breaking every -o json/-o yaml command. Make values addressable
first so the generated marshaler runs.
A CLI command issued right after startup can beat headscale to binding
its unix socket. Retry the dial until the CLI timeout, matching the old
blocking gRPC dial, instead of failing on a missing socket file.
Drop grpc_listen_addr/grpc_allow_insecure config, the gRPC auth integration
tests, GrpcSocketDialer, and stale gRPC comments now that the API is HTTP-only.
Delete proto/, gen/go/, gen/openapiv2/, and the buf config now that the v1 API
is served entirely by the ogen OpenAPI stack. Drop the proto lint/format targets
and refresh the vendor hash.
Decode CLI/HTTP output into apiv1 (ogen) types instead of the proto types,
matching the new wire format. No proto package is imported outside gen/go now.
Delete grpcv1.go, the TCP gRPC listener, the gRPC auth interceptor, and the
now-unused HTTP bearer middleware. The v1 API is served only over HTTP (TCP +
unix socket) by the ogen stack.
Convert directly from NodeView/UserView/PreAuthKeyView (no AsStruct copies, no
proto bridge), preserving the view types on the read path. Add UserView.Username
and drop the two state.go uses of PreAuthKey.Proto().
Serve the API over the unix socket (auth bypassed; socket permissions are the
trust boundary) and convert every CLI command from the gRPC client to the
generated ogen client. Remote CLI now uses the HTTP API URL.
Mount the ogen-generated server at /api/v1 with bearer-auth and RFC 7807
errors backed by the state layer; gRPC servers stay for the CLI. Add a
servertest API client harness and Health parity tests.
dockertest's bundled client builds against API v1.25; Docker Engine 29
rejects it (min 1.40), surfacing as a "broken pipe" that fails every local
image build. Pin to the daemon's reported version so builds use a live path.
Closes#2937
A nil tags slice marshals to JSON `null`; the clear-tagged migration
read that as tagged and cleared user_id. Exclude it, and recover
already-detached nodes from their pre-auth key.
Fixes#3323