Native Linux Pilot Deployment
Updated 9 September 2026
The verified pilot uses Node 24 on an existing Linux server. The same compiled application and SQLite format can run on a Linux VM in Azure. This is a single process deployment; cloud identity, disk encryption, backups and monitoring need separate configuration and validation.
Isolated Layout
| Item | Pilot Layout |
|---|---|
| Runtime | Node 24, npm ci --omit=dev --ignore-scripts, compiled dist/ |
| Account | Dedicated ocdit system user, no interactive login |
| Application | /opt/ocdit/app, root-owned, read-only to the service |
| Data | /opt/ocdit/data/live.sqlite, writable only by the service/admin |
| Credentials | /etc/ocdit/pilot.env, root-owned mode 600, loaded by systemd |
| Listener | HOST=127.0.0.1, PORT=4300 after checking the port is unused |
| Process | ocdit.service, restart on failure, 512 MB limit, 50% CPU quota |
| HTTPS | Dedicated nginx hostname/site and certificate; existing sites preserved |
The systemd service uses NoNewPrivileges, PrivateTmp, ProtectHome and
ProtectSystem=strict, with write access limited to the data directory. The
application runs node --max-old-space-size=256 dist/server.js from its app folder.
Public Routes
Proxy /api/messages, /webhooks/whatsapp, optional /webhooks/line and /webhooks/wechat, /healthz, /inbox/ and
/inbox-api/, /admin/ and /admin-api/ to the loopback listener. Redirect /admin
to /admin/. Inbox APIs require a validated Teams access token; Administration APIs
require an authorized browser session and mutation CSRF checks. Only the static
shells and sign-in flow are public. Include public/inbox/ and public/admin/ in runtime updates. Serve the supplied docs/pilot-site/ pages at /, /privacy and /terms.
Serve the generated public/guides/ directory at /guides/ only, redirect /guides to /guides/, and return 404 for other paths. Permit the ACME HTTP challenge and redirect other HTTP
requests to HTTPS. Suppress access logging on these routes so callback verification
tokens are not recorded in query strings. Keep provider secrets out of logs.
Use a dedicated certificate renewal configuration. After successful renewal, test nginx configuration before a graceful reload. Do not replace other server blocks, certificates, listeners or running applications.
Configuration and Verification
- Fill the required live values from
.env.examplein the private environment file. - Use the native Teams
channelData.team.idbeginning19:forTEAMS_TEAM_ID. The directory group GUID is not supplied by every card action. - Set only authorized staff Entra object IDs in
PILOT_ACTORS. - Start only
ocdit.service; confirm HTTPS health saysmode: live. - Confirm unsigned provider POSTs return 401 and demo routes return 404.
- Verify Meta's callback and subscribe the test WABA/app to
messages. - Install the generated Teams app in the configured private team; test a real round trip.
Day-to-Day Operation
- Check
systemctl status ocdit.serviceand/healthz; health alone does not prove delivery. - Inspect failed/uncertain jobs before retrying. Never blindly resend an ambiguous WhatsApp send.
- Use
@OCDIT inboxor Refresh on an old card to recover the latest stored case. - Before a code update, build/test locally, stage files, replace only the pilot files, restart only its service, and verify health plus delivery. Keep a rollback copy.
- Stop the service before a filesystem backup, or use SQLite's consistent backup API. Do not copy only the main database while WAL writes are active.
- Renew credentials before expiry. The demonstration's Microsoft secret expires 14 September 2026; its Meta user token expires 6 November 2026, 11:28 AM SGT. User tokens can also be invalidated early. Use a scoped service credential and tested rotation process before production.
Automated off-host backups, full-service recovery drills, host encryption evidence, mobile Teams and an Azure cutover rehearsal remain outstanding. The public pages describe a technical test; replace them with approved customer policies before real subsidiary traffic.
Inbox Sign-In Setup
Use only the isolated OCDIT app registration:
- In Expose an API, set
api://HOSTNAME/botid-CLIENT_IDas the identifier. - Add enabled delegated scope
access_as_user, with admin consent. - Preauthorize that scope for official Teams desktop/mobile client
1fec8e78-bce4-4aaf-ab1b-5451cc387264and web client5e3ce6c0-2b1f-4285-8d4b-75ee78787346after scoped access approval. - In the Microsoft Graph manifest, set
api.requestedAccessTokenVersionto2. - Build the Teams package with
scripts/package_teams.py; version 0.2.0 adds the personal Inbox tab, bot scope and matchingwebApplicationInfo. - Install for the enabled test employee. Verify sign-in, history, a real reply, delivery receipts and the personal bot alert. Keep the staff allowlist unchanged.
No Graph API permissions, tenant-wide policy changes or extra licenses are needed
by this implementation. The signed-in employee must still appear in PILOT_ACTORS.
Consistent database backup and isolated restore checks have passed. Follow Administrator Operations for the utility and recovery boundaries.