Eden REST API

VPN API

Source
ProtocolREST / JSON
Base path/api/v1
AuthenticationBearer token

The VPN API exposes Eden Gateway interlays over standard WireGuard clients. It is available when eden-service is built with the vpn feature.

VPN routes do not grant endpoint access by themselves. They expose an interlay listener and may lower the maximum data-plane permissions available through that listener. The peer's resolved user or agent subject must still be authorized through endpoint RBAC or ELS.

Networks

Create VPN Network

http
POST /api/v1/vpn/networks
Authorization: Bearer <token>
Content-Type: application/json
json
{
  "name": "prod-database-vpn",
  "interface_name": "wg-eden-prod",
  "address_cidr": "10.77.0.0/24",
  "listen_port": 51820,
  "eden_node_uuid": "00000000-0000-0000-0000-000000000001",
  "server_endpoint": "eden-prod.example.com:51820",
  "private_dns_domain": "db.vpn.example.com",
  "ingress_mode": "provider_managed",
  "ingress_provider_ref": "udp-lb/prod-database-vpn",
  "server_public_key": "<wireguard-server-public-key>",
  "server_private_key_ref": "secret://eden/vpn/prod",
  "standby_node_uuids": [
    "00000000-0000-0000-0000-000000000002"
  ]
}

Eden stores a server private-key reference, not private-key bytes, and omits the reference from responses. Use file:///absolute/path for a mounted file or secret://relative/name with EDEN_VPN_SECRET_ROOT. The key must exist with private filesystem permissions on every primary or standby node. In the active-active Helm profile, eden_node_uuid remains the initial placement record while every healthy VPN replica reconciles the same verified shared server identity. Use its stable UDP load-balancer address in server_endpoint. private_dns_domain is optional and enables peer-filtered authoritative DNS on UDP and TCP port 53 of the WireGuard server address. It must be a multi-label suffix and cannot use .local or localhost.

ingress_mode defaults to customer_managed. provider_managed requires server_endpoint, a non-secret ingress_provider_ref, and provider URL/token file deployment configuration on every candidate. The API never accepts or returns the provider credential. Eden trims server_endpoint and ingress_provider_ref; each must be at most 1024 bytes and contain no control characters.

Network creation persists desired state and can return before the controller has applied it. Poll the runtime-status route before distributing client configuration. The network definition—including ingress ownership, stable endpoint, provider reference, and replica topology—is immutable in the current API. There is no network patch route; create and validate a replacement network to change it.

List VPN Networks

http
GET /api/v1/vpn/networks
Authorization: Bearer <token>

Get VPN Network

http
GET /api/v1/vpn/networks/{network_uuid}
Authorization: Bearer <token>

Delete VPN Network

http
DELETE /api/v1/vpn/networks/{network_uuid}
Authorization: Bearer <token>

Deletion is durable. The controller keeps retrying interface and firewall cleanup until the runtime state reaches deleted. The interface name remains reserved on each candidate node until every candidate that may have applied it acknowledges cleanup, so a recovered former active node cannot remove a replacement interface with the same name. The endpoint returns 202 Accepted with network_uuid and status: "deleting"; poll the status route for completion.

Provider-managed deletion also waits for an acknowledgement that the fenced stable ingress has been withdrawn.

Get Runtime Status

http
GET /api/v1/vpn/networks/{network_uuid}/status
Authorization: Bearer <token>

The response includes desired_generation, observed_generation, status, last_error, last_reconciled_at, active_node_uuid, fencing_epoch, and lease_expires_at. Provider state is exposed as ingress_status, ingress_fencing_epoch, ingress_generation, ingress_active_node_uuid, ingress_operation_id, ingress_last_error, and ingress_updated_at. Provider-managed networks are available only when runtime is ready, the generations match, ingress is active, and its node/epoch match the current lease while ingress_generation matches desired_generation. Status remains queryable after a delete request so automation can wait for deleted.

json
{
  "network_uuid": "00000000-0000-0000-0000-000000000100",
  "desired_generation": 3,
  "observed_generation": 3,
  "status": "ready",
  "last_reconciled_at": "2026-07-25T15:00:00Z",
  "active_node_uuid": "00000000-0000-0000-0000-000000000001",
  "fencing_epoch": 7,
  "lease_expires_at": "2026-07-25T15:00:30Z",
  "ingress_status": "active",
  "ingress_fencing_epoch": 7,
  "ingress_generation": 3,
  "ingress_active_node_uuid": "00000000-0000-0000-0000-000000000001",
  "ingress_operation_id": "vpn-ingress-00000000-0000-0000-0000-000000000100-7-3-activate",
  "ingress_updated_at": "2026-07-25T15:00:00Z"
}

Runtime status is pending, applying, ready, error, deleting, or deleted; unknown is the fail-closed fallback for an unrecognized persisted value. ingress_status is customer_managed, pending, publishing, active, error, withdrawing, or withdrawn, with the same unknown fallback. Missing optional error and timestamp fields are omitted rather than serialized as null.

Get Replicas

http
GET /api/v1/vpn/networks/{network_uuid}/replicas
Authorization: Bearer <token>

The response contains the primary node and configured active/passive standby nodes. A standby automatically acquires the fenced lease after the active node's 30-second lease expires. The stable server_endpoint still needs customer-managed health routing in customer_managed mode. In provider_managed mode Eden publishes the fenced active node through the configured provider before reporting ready.

Replica topology is immutable for the lifetime of a network. Replace the network to change candidates safely.

Get or Replace Device Access Policy

http
GET /api/v1/vpn/networks/{network_uuid}/access-policy
PUT /api/v1/vpn/networks/{network_uuid}/access-policy
Authorization: Bearer <admin-token>
json
{
  "enrollment_mode": "admin_approval",
  "enrollment_ttl_seconds": 900,
  "access_ttl_seconds": 86400,
  "key_ttl_seconds": 2592000,
  "require_external_identity": true,
  "require_managed_device": true,
  "require_disk_encryption": true,
  "require_screen_lock": true,
  "require_endpoint_protection": true,
  "posture_max_age_seconds": 600,
  "allowed_platforms": ["linux", "macos", "windows"]
}

Use admin_approval or automatic. Posture rules must include a freshness window. The default policy requires admin approval but does not require external identity, posture, or expiring access.

Replacing a policy immediately re-evaluates approved enrolled peers. Peers that fail new identity or posture rules are deactivated; relaxing the policy does not silently reactivate them.

Request Device Enrollment

http
POST /api/v1/vpn/networks/{network_uuid}/device-enrollments
Authorization: Bearer <device-owner-token>
Content-Type: application/json
json
{
  "name": "devon-laptop",
  "public_key": "<wireguard-client-public-key>",
  "requested_tunnel_ip": null
}

The authenticated user or agent is the owner; the request cannot select another subject or assert posture. Eden trusts posture only from the verified OIDC device-posture claim. Automatic policies approve and create the peer in the same operation when all rules pass.

Administrators can list requests with:

http
GET /api/v1/vpn/networks/{network_uuid}/device-enrollments

Owners can inspect their own request with:

http
GET /api/v1/vpn/device-enrollments/{enrollment_uuid}

Approve or Reject Device Enrollment

http
POST /api/v1/vpn/device-enrollments/{enrollment_uuid}/approval
POST /api/v1/vpn/device-enrollments/{enrollment_uuid}/rejection
Authorization: Bearer <admin-token>
json
{
  "reason": "approved production workstation",
  "tunnel_ip": null
}

Decisions are single-use. Approval atomically creates the peer and lifecycle lease and fails when the request or stored posture has expired. Rejection accepts {"reason":"device is not in the approved inventory"}.

Peers

Create VPN Peer

http
POST /api/v1/vpn/networks/{network_uuid}/peers
Authorization: Bearer <token>
Content-Type: application/json
json
{
  "name": "devon-laptop",
  "subject_kind": "user",
  "subject_uuid": "20000000-0000-0000-0000-000000000002",
  "public_key": "<wireguard-client-public-key>",
  "tunnel_ip": null
}

When tunnel_ip is null, Eden allocates the next available peer address inside the network CIDR.

List VPN Peers

http
GET /api/v1/vpn/networks/{network_uuid}/peers
Authorization: Bearer <token>

Update VPN Peer

http
PATCH /api/v1/vpn/peers/{peer_uuid}
Authorization: Bearer <token>
Content-Type: application/json
json
{
  "name": "devon-new-laptop",
  "public_key": "<new-wireguard-client-public-key>",
  "tunnel_ip": "10.77.0.22"
}

For enrolled device peers, this route cannot change the public key or active state. Use key rotation or revocation so lifecycle policy cannot be bypassed.

Revoke VPN Peer

http
DELETE /api/v1/vpn/peers/{peer_uuid}
Authorization: Bearer <token>

Render VPN Client Config

http
GET /api/v1/vpn/peers/{peer_uuid}/config
Authorization: Bearer <token>

Eden returns a standard WireGuard config. AllowedIPs contains only routes the peer subject can use through endpoint RBAC or ELS. The enrolled peer owner may fetch its own config; other callers need control-plane read permission.

When private DNS is enabled, the config also contains DNS = and routes that resolver address through WireGuard.

Get Peer Discovery

http
GET /api/v1/vpn/peers/{peer_uuid}/discovery
Authorization: Bearer <peer-owner-or-admin-token>

Returns dns_server, dns_domain, and only the route records authorized to the peer's user or agent subject. Named records contain dns_name; unnamed routes remain present with their listener and vpn_ip.

Get Peer Lifecycle

http
GET /api/v1/vpn/peers/{peer_uuid}/lifecycle
Authorization: Bearer <peer-owner-or-admin-token>

Returns access, posture, key, and revocation state for an enrolled peer.

Rotate Peer Key

http
POST /api/v1/vpn/peers/{peer_uuid}/key-rotations
Authorization: Bearer <peer-owner-or-admin-token>
Content-Type: application/json
json
{
  "public_key": "<replacement-wireguard-client-public-key>"
}

Eden increments the key generation, applies the current key TTL, and reconciles WireGuard. The client generates and retains the replacement private key. Rotation cannot reactivate revoked or access/posture-expired peers.

Routes

Create VPN Route

http
POST /api/v1/vpn/networks/{network_uuid}/routes
Authorization: Bearer <token>
Content-Type: application/json
json
{
  "interlay": "prod-mysql-vpn",
  "listener_id": "mysql",
  "vpn_ip": "10.77.0.3",
  "dns_label": "orders",
  "eden_node_uuid": "00000000-0000-0000-0000-000000000001",
  "max_data_perms": "r"
}

vpn_ip must be inside the VPN network CIDR and must match the selected interlay listener bind_host. max_data_perms is a ceiling and cannot elevate the subject beyond RBAC or ELS; it defaults to read-only when omitted. dns_label is optional and requires private_dns_domain on the network. It is a single label, unique among active routes, and publishes a peer-filtered name such as orders.db.vpn.example.com.

List VPN Routes

http
GET /api/v1/vpn/networks/{network_uuid}/routes
Authorization: Bearer <token>

Delete VPN Route

http
DELETE /api/v1/vpn/routes/{route_uuid}
Authorization: Bearer <token>

Endpoint Node Placement

VPN route creation requires the primary and every configured standby Eden node to be registered for the interlay endpoint and in an available or degraded connection status. In the active-active Helm VPN profile, every healthy replica also converges the route-backed listener so a surviving VPN endpoint can deliver re-handshaken traffic.

http
GET /api/v1/endpoints/{endpoint}/nodes
PUT /api/v1/endpoints/{endpoint}/nodes/{eden_node_uuid}
Help improve Eden Docs

Find something unclear or incomplete? Review the source and propose an update.

View on GitLab Updated August 2, 2026