No description
Find a file
2026-04-14 15:45:17 +08:00
edge_auth Include name in response 2026-04-14 15:45:17 +08:00
systemd Initial version 2026-03-30 21:19:47 +08:00
.gitignore Initial version 2026-03-30 21:19:47 +08:00
example.env Initial version 2026-03-30 21:19:47 +08:00
pyproject.toml Initial version 2026-03-30 21:19:47 +08:00
README.md Include name in response 2026-04-14 15:45:17 +08:00
uv.lock Initial version 2026-03-30 21:19:47 +08:00

member_portal-edge_auth

Helper that interfaces between portal access lists and edge/access control hardware.

Requirements

  • Linux (comms over socket)
  • Python 3.12+
  • Network access to a member portal instance

Configuration

An example .env is provided as example.env

Variable Required Description
EDGE_AUTH_API_KEY yes Portal API key (X-API-Key)
EDGE_AUTH_ACCESS_LIST yes Access list short_name
EDGE_AUTH_BASE_URL no Default http://localhost:5000
EDGE_AUTH_SOCKET_PATH no Default run/member-portal-edge-auth.sock (relative to cwd; parent directory is created)
EDGE_AUTH_SOUND_CACHE_DIR no Default data/sounds (relative to cwd)
EDGE_AUTH_CACHE_FILE no JSON file for last successful access list (default data/access_list_cache.json); loaded if startup refresh fails
EDGE_AUTH_OUTBOX_FILE no JSONL queue for failed portal writes (default data/pending_scans.jsonl)
EDGE_AUTH_REPLAY_INTERVAL_SECONDS no How often to retry the outbox (default 60)
EDGE_AUTH_REVISION_POLL_SECONDS no How often to poll GET .../revision (default 30)
EDGE_AUTH_FULL_REFRESH_SECONDS no Full list refresh interval (default 3600)
EDGE_AUTH_DEVICE_ID no Label for timeline / denial payloads (default edge)
EDGE_AUTH_EVENT_TYPE_GRANTED no Timeline event_type for allowed taps (default edge_access_granted)

Run

From the deploy directory:

uv sync
uv run member-portal-edge-auth

systemd

A sample unit file is in systemd/member-portal-edge-auth.service. It uses uv run member-portal-edge-auth with WorkingDirectory set to the deploy tree (so pyproject.toml, uv.lock, and .env are used the same way as interactive runs). ExecStart must point at the uv binary (e.g. /usr/local/bin/uv); install or symlink it there, or edit the unit. See comments in the unit file for install steps.

Offline behaviour and persistence

  • Access list file: After a successful fetch from the portal, the current card map is written to EDGE_AUTH_CACHE_FILE. If the daemon starts and cannot reach the portal, it loads that file when the in-memory cache is still empty (same EDGE_AUTH_ACCESS_LIST as when the file was saved).
  • Outbox: If a timeline grant or access-denial POST fails (network error or non-success HTTP), the request body is appended to EDGE_AUTH_OUTBOX_FILE (JSON lines). A background task retries on a schedule and after each successful access list refresh. Each line includes an ISO 8601 timestamp so the portal can record when the scan happened, not only when replay succeeded (POST /api/timeline/events and POST /api/edge/access-denial both accept timestamp).
  • Sound cache: After each successful refresh, newly referenced door sounds are fetched into the cache, and MP3 files no longer referenced by the current list are deleted.

Test socket client

With the edge-auth daemon running, send a card id and print the JSON response (uses EDGE_AUTH_SOCKET_PATH or the same default as the daemon):

uv run edge-auth-test-socket "30:01:02:bb"
# or
uv run edge-auth-test-socket 3137470768

The test client sends card as a JSON string unchanged. That value must match the portals stored card_number for the tap to be allowed (quote in the shell when needed, e.g. "00123").

Optional: uv run edge-auth-test-socket --socket-path /path/to/sock 3137470768

Socket protocol

One JSON object per line (newline-terminated), one JSON response per line.

Authorise

{"card":"30:01:02:bb"}

or {"cmd":"authorise","card":"..."}.

Use a JSON string that matches the portal card_number (readers/scanners should perform any format conversion before sending). A JSON number cannot preserve leading zeros and may not match a zero-padded stored card number.

Response (allowed)

{"allowed":true,"name":"Alex","sound_path":"/path/to/deploy/data/sounds/uuid.mp3"}

Response (denied)

{"allowed":false,"portal":{"logged":"unknown_scan"}}

If the portal reports the card is on the access list but the edge cache was stale, portal may include "logged":"skipped","reason":"card_on_access_list"; the helper then refreshes its cache.

Force refresh

{"cmd":"refresh"}