Files
QuantEngineByItz/docs/SYNOLOGY_SNAPSHOT_ADMIN_POC.md
T

7.2 KiB

Synology Snapshot Admin POC

This guide enables external access to the Python snapshot admin service on Synology without exposing the raw service port to the internet.

  1. Keep the Python service bound to loopback only:
python tools/run_snapshot_admin_server_v1.py \
  --host 127.0.0.1 \
  --port 8787 \
  --db outputs/snapshot_admin/snapshot_admin.db \
  --seed GatherTradingData.json
  1. Put Synology DSM reverse proxy in front of it:

    • Source: https://<public-host>:443
    • Destination: http://127.0.0.1:8787
    • Keep the service port closed from direct WAN access.
  2. Add browser authentication with the built-in Basic Auth gate:

    • Set SNAPSHOT_ADMIN_AUTH_USER
    • Set SNAPSHOT_ADMIN_AUTH_PASSWORD
    • Or pass --auth-user and --auth-password on the wrapper command
  3. Verify from the NAS:

curl -i http://127.0.0.1:8787/api/state
curl -u "$SNAPSHOT_ADMIN_AUTH_USER:$SNAPSHOT_ADMIN_AUTH_PASSWORD" http://127.0.0.1:8787/api/state
  1. Verify from outside the NAS:
    • Open https://<public-host>/
    • The browser should prompt for Basic Auth
    • https://<public-host>/tables should render after login

DSM Checklist

Use these exact values for the first POC.

  1. DSM app path

    • Control Panel
    • Login Portal
    • Advanced
    • Reverse Proxy
  2. Create reverse proxy rule

    • Description: snapshot-admin
    • Source protocol: HTTPS
    • Source hostname: your public DNS name, for example admin.example.com
    • Source port: 443
    • Source path: /
    • Destination protocol: HTTP
    • Destination hostname: 127.0.0.1
    • Destination port: 8787
  3. Certificate

    • Attach a valid TLS certificate for the public hostname
    • Prefer a Synology-managed or imported certificate that matches admin.example.com
  4. Firewall

    • Allow inbound 443/TCP only for the reverse proxy endpoint
    • Do not expose 8787/TCP on WAN
    • If the NAS must be reachable only from a VPN or office IP range, allowlist those ranges and block the rest
  5. Service start policy

    • Start the Python service on boot or via DSM Task Scheduler
    • Keep it bound to 127.0.0.1 unless you intentionally use direct bind mode
    • If you use direct bind mode, keep --allow-remote and Basic Auth enabled together
    • For Gitea Actions runner verification, register act_runner with a dedicated host label (self-hosted:host,snapshot-admin-host:host) if you want to avoid Docker job containers and the Cleaning up container log line
    • Preferred launcher script: tools/run_snapshot_admin_synology.sh
    • Gitea CI deploy path: trigger .gitea/workflows/snapshot_admin_deploy.yml workflow_dispatch and let the host runner call the launcher script
    • Runner bootstrap: tools/re_register_act_runner_synology.sh
    • Runner daemon start: tools/start_act_runner_synology.sh
  6. Runner re-registration

    • Use this when you want to switch an existing runner from Docker mode to host mode:
cd /volume1/projects/data_feed
REG_TOKEN="<runner-registration-token>" \
GITEA_URL="http://192.168.123.100:8418" \
bash tools/re_register_act_runner_synology.sh
  • Expected effect:
    • removes the existing .runner registration file
    • registers self-hosted:host,snapshot-admin-host:host
    • writes an updated config.yaml
  • If the old runner remains listed in Gitea, remove it from the repository runner page and re-run the command above
  1. Runner start
    • After re-registration, start the daemon:
bash tools/start_act_runner_synology.sh
  • Expected effect:
    • launches act_runner daemon using the existing config
    • records runner.pid and runner.log under the runner directory

DSM Task Scheduler

Create two scheduled tasks in Control Panel > Task Scheduler.

  1. Boot task
    • Task name: snapshot-admin-start
    • User: root or a dedicated service account with access to the project folder
    • Event: Boot-up
    • Command:
bash /volume1/projects/data_feed/tools/run_snapshot_admin_synology.sh start
  1. Healthcheck task
    • Task name: snapshot-admin-healthcheck
    • User: same as boot task
    • Event: Scheduled Task
    • Repeat: every 5 minutes
    • Command:
bash /volume1/projects/data_feed/tools/run_snapshot_admin_synology.sh healthcheck
  1. Manual restart task
    • Task name: snapshot-admin-restart
    • User: same as boot task
    • Event: Scheduled Task
    • Repeat: manual only, or keep disabled until needed
    • Command:
bash /volume1/projects/data_feed/tools/run_snapshot_admin_synology.sh restart

Direct bind mode

Direct binding to 0.0.0.0 is allowed only when both auth values are configured:

python tools/run_snapshot_admin_server_v1.py \
  --host 0.0.0.0 \
  --port 8787 \
  --allow-remote \
  --auth-user "$SNAPSHOT_ADMIN_AUTH_USER" \
  --auth-password "$SNAPSHOT_ADMIN_AUTH_PASSWORD"

Use this only if you have a separate firewall or VPN rule in place. The default POC path is still loopback + reverse proxy.

Validation

Run the unit/web checks before and after deployment:

python -m pytest tests/unit/test_snapshot_admin_web_v1.py -q
python tools/validate_snapshot_admin_web_v1.py

The auth gate is part of the service now, so public exposure without credentials is rejected by the server itself.

Curl checklist

Use this as the POC run sheet.

  1. Local service check:
curl -i http://127.0.0.1:8787/api/state

Expected:

  • 200 OK
  • JSON payload contains version.app
  1. Reverse proxy auth challenge:
curl -i https://<public-host>/api/state

Expected:

  • 401 Unauthorized
  • WWW-Authenticate: Basic
  1. Reverse proxy authenticated access:
curl -u '<user>:<password>' https://<public-host>/api/state

Expected:

  • 200 OK
  • JSON payload contains the same version.app
  1. UI rendering:
curl -I https://<public-host>/
curl -I https://<public-host>/tables

Expected:

  • 200 OK after auth
  • HTML response, not a redirect to the raw port
  1. Restart persistence:
bash tools/run_snapshot_admin_synology.sh restart
bash tools/run_snapshot_admin_synology.sh healthcheck

Expected:

  • healthcheck ok
  • The proxy URL continues to answer after the service restarts

Live verification

Use this sequence on the actual Synology box after the reverse proxy rule is in place:

  1. Start the service and confirm the local health endpoint:
curl -i http://127.0.0.1:8787/api/state
  1. Confirm the auth gate:
curl -i https://<public-host>/api/state

Expected result:

  • 401 Unauthorized when no credentials are provided
  • 200 OK when valid Basic Auth credentials are supplied
  1. Confirm the browser surface:

    • Open https://<public-host>/
    • Sign in with the Basic Auth credentials
    • Open https://<public-host>/tables
    • Confirm rows render from the three SQLite sources
  2. Confirm the deployment survives a process restart:

    • Restart the Python service or the task that launches it
    • Re-run curl -i http://127.0.0.1:8787/api/state
    • Re-open the browser URL and confirm login still works
  3. Archive evidence:

    • Save the curl outputs
    • Save a screenshot of / and /tables
    • Record the DSM reverse proxy rule values and certificate name