ACL & access control
The Access Control List (ACL) defines which members can access which apps. It’s managed in atreoLINK and synced to the agent as a complete, user-signed device state that the agent verifies and reconciles wholesale.
ACL structure
Section titled “ACL structure”The ACL is a list of member entries, each containing:
{ "memberId": "uuid", "memberName": "John", "role": "admin", "identityKey": "<base64 Ed25519 pubkey>", "clients": [ { "wgPublicKey": "<base64 Curve25519>", "tunnelIp": "100.64.0.2", "label": "John's iPhone", "platform": "ios" } ], "allowedApps": [ { "id": "app-id", "name": "Jellyfin", "slug": "jellyfin", "internalUrl": "http://localhost:8096" }, { "id": "app-id-2", "name": "Minecraft", "slug": "minecraft", "type": "port", "port": 25565, "protocol": "tcp" } ], "status": "active", "admittance": { "...": "see Authentication / Tunnel Protocol" }}Each WireGuard client (phone, tablet, browser) is one entry in clients, with its public key, tunnel IP, and metadata in one atomic record. The overlay is dual-stack, so each client also gets the matching fd00:64::x IPv6 address alongside its 100.64.0.x address. Owner and admin rows skip admittance; the owner identity is anchored separately at pair time.
| Role | Permissions |
|---|---|
admin | Access to all apps, full management in atreoLINK. |
member | Access only to apps listed in allowedApps. |
Enforcement
Section titled “Enforcement”When a request comes through the reverse proxy or forward-auth endpoint:
- The source tunnel IP is looked up in the ACL index.
- The member’s
statusmust beactive. - The requested app slug (from the hostname) must be in the member’s
allowedApps. - If all checks pass, the request is allowed. On the forward-auth path the response carries
X-Auth-User,X-Auth-Member-ID, andX-Auth-Roleheaders for the calling proxy; the built-in reverse proxy enforces the ACL itself and forwards the request without adding these headers.
Requests from trusted networks bypass ACL checks entirely.
Exposed-port apps
Section titled “Exposed-port apps”Exposed-port apps’ traffic doesn’t flow through the reverse proxy, so they’re enforced one layer lower — in the agent’s per-peer firewall, not at the HTTP request. From the ACL the agent derives a port grant for each active member: the TCP and UDP ports of the port-type apps in that member’s allowedApps, keyed by each of the member’s client tunnel IPs (both IPv4 and IPv6). It then programs iptables and ip6tables to accept traffic from those tunnel IPs to exactly those host ports, and nothing else.
- A member who isn’t
active, or who isn’t granted a given port app, gets no rule for that port — packets are dropped. httpandhttpsport apps are opened as TCP; the protocol is only a hint to the client to open a browser link.- The one thing the proxy answers for a port app is the slug URL of an
http/httpsport app: a redirect to the service’shost:port, issued only when the requesting tunnel IP holds the port grant (or the source is a trusted network) — the same condition that opens the firewall. - Grants are recomputed and the firewall reprogrammed on every ACL change, the same way WireGuard peers are reconciled.
Syncing
Section titled “Syncing”The ACL is kept in sync through state-based reconciliation. atreoLINK is the source of truth for which envelopes are current; it never invents their contents.
atreoLINK stores and pushes signed state. atreoLINK keeps the latest user-signed envelope per entity and dimension (a member’s status, a member’s permissions, a member’s clients, each app, the active custom domain). It assembles them into a single signed DeviceState and pushes the complete state to the agent on connect and again on every change. atreoLINK signs nothing itself and verifies nothing; it relays each envelope verbatim.
The agent verifies, then reconciles wholesale. The agent verifies every item in the DeviceState against the owner identity key it pinned at pairing. For each non-owner member the admittance field is a durable certificate the agent itself minted on first admission; on every later push the agent verifies that certificate against its own pinned identity key rather than re-walking the original invite. In the brief window between an invitee accepting and the agent’s first admittance report, the slot carries the acceptance-time joinAttestation instead. The agent then reconciles its entire world to match:
- Anything absent from the new state is removed. A member who is no longer present has their WireGuard peer dropped immediately.
- A member’s status, permissions, and clients are taken from that member’s signed envelopes, not inferred and not supplied by atreoLINK.
- A monotonic
generationcounter on the state lets the agent reject any state that is strictly older than what it already holds.
Persistent cache. The agent persists its last verified state to /var/lib/atreoagent/acl.json so it can keep enforcing access if atreoLINK is briefly unreachable.
Trust boundary
Section titled “Trust boundary”The split of responsibility is deliberate and narrow:
- Every ACL item is signed by a user (the owner, or the member for self-scoped actions). atreoLINK relays and serves these signed envelopes verbatim and verifies none of them.
- The agent verifies every item against the owner identity key pinned at pairing before it acts on anything.
Because of this, atreoLINK cannot forge a member, add a stranger to the ACL, alter app permissions, or read the WireGuard tunnel. Doing any of those would require a signature it cannot produce.
Indexes
Section titled “Indexes”The agent maintains two in-memory indexes for fast lookups:
- By tunnel IP. Used during request authentication (O(1) lookup).
- By member ID. Used during client provisioning and management.
Managing access
Section titled “Managing access”Access is managed through the atreoLINK dashboard:
- Add a member. Creates an ACL entry with their identity key.
- Assign apps. Select which apps the member can access.
- Remove access. Remove apps or deactivate the member.
- Remove member. Removes all access and WireGuard peers.
See also
Section titled “See also”- Trusted networks for the LAN-bypass behaviour.
- Authentication for how ACL membership is verified.
© 2026 atreoLABS. All rights reserved.
WireGuard is a registered trademark of Jason A. Donenfeld.

