Files
gatehouse-api/docs/zerotier-network-lifecycle.md

6.2 KiB

ZeroTier Network Lifecycle

Overview

This document covers the full lifecycle of ZeroTier networks in Gatehouse — how networks are created, who can see them, how members request access, and how devices are activated and deactivated.

Organization Membership Roles

Every user belongs to an organization via an OrganizationMember record. Roles determine what a user can see and do:

Role Can list networks? Can see invite-only networks? Can create/update/delete networks? Can approve access requests?
owner Yes Yes Yes Yes
admin Yes Yes Yes Yes
member Yes No No No
guest Yes No No No

Role checks happen via the OrganizationMember.is_admin() method, which returns True for owner and admin.

Network Request Modes

Every PortalNetwork has a request_mode field that controls how users gain access:

Mode Value Behavior
open "open" Any org member can join directly without approval
approval_required "approval_required" User requests access; a manager must approve
invite_only "invite_only" Only managers can assign access; invisible to non-admins

Network Listing Visibility

GET /organizations/{org_id}/networks

The listing endpoint applies two visibility filters:

  1. Soft-delete filter — networks with a non-null deleted_at are always excluded.
  2. Active filter — by default, only networks where is_active = True are returned. Pass ?include_inactive=true to include disabled networks.
  3. Invite-only filter — networks with request_mode = "invite_only" are hidden from non-admin users (member and guest roles). Admins and owners see all networks.

Filtering logic

The filtering happens in portal_network_service.list_networks():

# Non-admin users cannot see invite-only networks
if user_id is not None:
    membership = OrganizationMember.query.filter(...).first()
    is_admin = membership.is_admin() if membership else False
    if not is_admin:
        q = q.filter(PortalNetwork.request_mode != NetworkRequestMode.INVITE_ONLY)

Network CRUD

Action Endpoint Required Role
List networks GET /organizations/{id}/networks Any org member (visibility restricted as above)
Create network POST /organizations/{id}/networks admin or owner
Update network PUT /organizations/{id}/networks/{id} admin or owner
Delete network DELETE /organizations/{id}/networks/{id} admin or owner

Device Registration

Before a user can access a network, they must register a device:

POST /organizations/{org_id}/devices

A Device record ties a ZeroTier node (10-char node_id) to a user within an org.

Field Purpose
node_id ZeroTier 10-char node identifier
device_nickname Human-friendly label
hostname Optional hostname for identification

Network Access Request Lifecycle

The core model is NetworkAccessRequest (table: network_access_requests). Each row represents one user + one device + one network. See zerotier-device-membership.md for the full schema.

Flow by request mode

Open networks — user calls join_network_for_device() directly:

  1. Creates NetworkAccessRequest with status=APPROVED, active=False
  2. Returns the request

Approval-required networks — user calls request_access():

  1. Creates NetworkAccessRequest with status=PENDING
  2. Admin calls approve_request() → sets status=APPROVED
  3. User calls activate_membership() → sets active=True, creates ActivationSession

Invite-only networks — only an admin can call assign_access():

  1. Admin creates NetworkAccessRequest with status=APPROVED, grant_type=ASSIGNED
  2. User calls activate_membership() → sets active=True, creates ActivationSession

The active flag

status active Meaning
approved false Has permission but not currently connected
approved true Has permission and device is authorized on the controller
pending false Awaiting approval
rejected / revoked / suspended false Access denied or removed

Activation and Deactivation

Activation creates an ActivationSession with a configurable TTL (default 8 hours). The session is tied to the active=True state.

  • activate_membership() — sets active=True, creates session, authorizes on ZeroTier controller
  • deactivate_membership() — sets active=False, ends session, de-authorizes on controller
  • Activation sessions expire automatically via the reconciliation worker, which sets active=False

Kill switch

Admins can trigger a kill switch to deactivate all active memberships on an organization or network:

  • POST /organizations/{id}/kill-switch — deactivates all memberships in the org
  • POST /organizations/{id}/networks/{id}/kill-switch — deactivates all memberships on a specific network

Reconciliation Worker

A scheduled job (runs every 2 minutes) performs:

  1. Expired activation cleanup — finds expired ActivationSession records, de-authorizes in ZeroTier, sets active=False
  2. Drift detection — compares portal state against ZeroTier controller state, repairs mismatches

Key Source Files

File Purpose
gatehouse_app/models/zerotier/portal_network.py PortalNetwork model (network definition + request_mode)
gatehouse_app/models/zerotier/network_access_request.py NetworkAccessRequest model (per-device membership)
gatehouse_app/models/zerotier/activation_session.py ActivationSession model (TTL-based sessions)
gatehouse_app/models/zerotier/device.py Device model
gatehouse_app/models/organization/organization_member.py OrganizationMember model (roles)
gatehouse_app/services/portal_network_service.py Network CRUD + listing logic
gatehouse_app/services/network_access_service.py Access request + activation logic
gatehouse_app/services/zerotier_reconciliation_service.py Expired session + drift reconciliation
gatehouse_app/api/v1/zerotier.py All ZeroTier API endpoints
gatehouse_app/utils/constants.py Enums (OrganizationRole, NetworkRequestMode, etc.)