Eden REST API
VPN API
/api/v1The 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
POST /api/v1/vpn/networks
Authorization: Bearer <token>
Content-Type: application/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
GET /api/v1/vpn/networks
Authorization: Bearer <token>Get VPN Network
GET /api/v1/vpn/networks/{network_uuid}
Authorization: Bearer <token>Delete VPN Network
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
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.
{
"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
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
GET /api/v1/vpn/networks/{network_uuid}/access-policy
PUT /api/v1/vpn/networks/{network_uuid}/access-policy
Authorization: Bearer <admin-token>{
"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
POST /api/v1/vpn/networks/{network_uuid}/device-enrollments
Authorization: Bearer <device-owner-token>
Content-Type: application/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:
GET /api/v1/vpn/networks/{network_uuid}/device-enrollmentsOwners can inspect their own request with:
GET /api/v1/vpn/device-enrollments/{enrollment_uuid}Approve or Reject Device Enrollment
POST /api/v1/vpn/device-enrollments/{enrollment_uuid}/approval
POST /api/v1/vpn/device-enrollments/{enrollment_uuid}/rejection
Authorization: Bearer <admin-token>{
"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
POST /api/v1/vpn/networks/{network_uuid}/peers
Authorization: Bearer <token>
Content-Type: application/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
GET /api/v1/vpn/networks/{network_uuid}/peers
Authorization: Bearer <token>Update VPN Peer
PATCH /api/v1/vpn/peers/{peer_uuid}
Authorization: Bearer <token>
Content-Type: application/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
DELETE /api/v1/vpn/peers/{peer_uuid}
Authorization: Bearer <token>Render VPN Client Config
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
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
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
POST /api/v1/vpn/peers/{peer_uuid}/key-rotations
Authorization: Bearer <peer-owner-or-admin-token>
Content-Type: application/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
POST /api/v1/vpn/networks/{network_uuid}/routes
Authorization: Bearer <token>
Content-Type: application/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
GET /api/v1/vpn/networks/{network_uuid}/routes
Authorization: Bearer <token>Delete VPN Route
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.
GET /api/v1/endpoints/{endpoint}/nodes
PUT /api/v1/endpoints/{endpoint}/nodes/{eden_node_uuid}