TOD (The Other Dude) implements a 1Password-inspired zero-knowledge security architecture. The server never stores or sees user passwords. All data is stored on infrastructure you own and control — no external telemetry, analytics, or third-party data transmission.
TOD uses the Secure Remote Password (SRP-6a) protocol for authentication, ensuring the server never receives, transmits, or stores user passwords.
- SRP-6a protocol: Password is verified via a zero-knowledge proof — only a cryptographic verifier derived from the password is stored on the server, never the password itself.
- Two-Secret Key Derivation (2SKD): Combines the user password with a 128-bit Secret Key using a multi-step derivation process, ensuring that compromise of either factor alone is insufficient.
- Key derivation pipeline: PBKDF2 with 650,000 iterations + HKDF expansion + XOR combination of both factors.
- Secret Key format:
A3-XXXXXX(128-bit), stored exclusively in the browser's IndexedDB. The server never sees or stores the Secret Key. - Emergency Kit: Downloadable PDF containing the Secret Key for account recovery. Generated client-side.
- Vault key decryption on login: After successful SRP authentication, the client decrypts the user's vault key using the derived session key. This enables client-side decryption of encrypted data without the server ever handling the plaintext vault key.
- Session management: JWT tokens with 15-minute access token lifetime and 7-day refresh token lifetime. Access tokens are returned in the response body; refresh tokens are delivered via httpOnly cookies to enable silent token refresh without exposing the refresh token to JavaScript.
- SRP session state: Ephemeral SRP handshake data stored in Redis with automatic expiration.
Client Server
| |
| POST /auth/srp/init {email} |
|------------------------------------>|
| {salt, server_ephemeral_B} |
|<------------------------------------|
| |
| [Client derives session key from |
| password + Secret Key + salt + B] |
| |
| POST /auth/srp/verify {A, M1} |
|------------------------------------>|
| [Server verifies M1 proof] |
| {M2, access_token, refresh_token} |
|<------------------------------------|
Device credentials (RouterOS usernames and passwords) are encrypted at rest using envelope encryption:
- Encryption algorithm: Fernet symmetric encryption (AES-128-CBC + HMAC-SHA256).
- Key management: OpenBao Transit secrets engine provides the master encryption keys.
- Per-tenant isolation: Each tenant has its own encryption key in OpenBao Transit.
- Envelope encryption: Data is encrypted with a data encryption key (DEK), which is itself encrypted by the tenant's Transit key.
- Go poller decryption: The poller service decrypts credentials at runtime via the Transit API, with an LRU cache (1,024 entries, 5-minute TTL) to reduce KMS round-trips.
- CA private keys: Encrypted with AES-256-GCM before database storage. PEM key material is never logged.
Multi-tenancy is enforced at the database level, making cross-tenant data access structurally impossible:
- PostgreSQL Row-Level Security (RLS): All data tables have RLS policies that filter rows by
tenant_id. app_userdatabase role: All application queries run through a non-superuser role that enforces RLS. Even a SQL injection attack cannot cross tenant boundaries.- Session context:
tenant_idis set via PostgreSQL session variables (SET app.current_tenant) on every request, derived from the authenticated user's JWT. super_adminrole: Users with NULLtenant_idcan access all tenants for platform administration. Represented as'super_admin'in the RLS context.poller_userrole: Bypasses RLS by design — the polling service needs cross-tenant device access to poll all devices. This is an intentional security trade-off documented in the architecture.
| Role | Scope | Capabilities |
|---|---|---|
super_admin |
Global | Full system access, tenant management, user management across all tenants |
admin |
Tenant | Manage devices, users, settings, certificates within their tenant |
operator |
Tenant | Device operations, configuration changes, monitoring |
viewer |
Tenant | Read-only access to devices, metrics, and dashboards |
- RBAC is enforced at both the API middleware layer and database level.
- API keys inherit the
operatorpermission level and are scoped to a single tenant. - API key tokens use the
mktp_prefix and are stored as SHA-256 hashes (the plaintext token is shown once at creation and never stored).
TOD includes a per-tenant Internal Certificate Authority for managing TLS certificates on RouterOS devices:
- Per-tenant CA: Each tenant can generate its own self-signed Certificate Authority.
- Device certificate lifecycle: Certificates follow a state machine:
issued->deploying->deployed->expiring/revoked/superseded. - Deployment: Certificates are deployed to devices via SFTP.
- Three-tier TLS fallback: The Go poller attempts connections in order:
- CA-verified TLS (using the tenant's CA certificate)
- InsecureSkipVerify TLS (for self-signed RouterOS certs)
- Plain API connection (fallback)
- Key protection: CA private keys are encrypted with AES-256-GCM before database storage. PEM key material is never logged or exposed via API responses.
- Certificate rotation and revocation: Supported via the certificate lifecycle state machine.
TOD includes on-demand WinBox tunnels and browser-based SSH terminals for devices behind NAT.
- Single-use session tokens: SSH sessions are initiated with a short-lived token stored in Redis (
GETDEL, 120-second TTL). The token is consumed on first use and cannot be replayed. - RBAC enforcement: Opening a tunnel or starting an SSH session requires the
operatorrole or higher.vieweraccounts have no access to remote access features. - Audit trail: Tunnel open/close events and SSH session start/end events are recorded in the immutable audit log with device ID, user ID, source IP, and timestamp.
- WinBox tunnel binding: TCP proxies for WinBox connections are bound to
127.0.0.1only. Tunnels are never exposed on0.0.0.0and cannot be reached from outside the host without explicit port forwarding. - Idle-timeout cleanup: Inactive tunnels are closed automatically after
TUNNEL_IDLE_TIMEOUTseconds (default 300). SSH sessions time out afterSSH_IDLE_TIMEOUTseconds (default 900). Resources are reclaimed immediately on disconnect.
- RouterOS communication: All device communication uses the RouterOS binary API over TLS (port 8729). InsecureSkipVerify is enabled by default because RouterOS devices typically use self-signed certificates. To eliminate this risk, use the Internal Certificate Authority feature to issue verified TLS certificates to your devices.
- CORS enforcement: Strict CORS policy in production, configured via
CORS_ORIGINSenvironment variable. - Rate limiting: Authentication endpoints are rate-limited to 5 requests per minute per IP to prevent brute-force attacks.
- Cookie security: httpOnly cookies prevent JavaScript access to session tokens. The
Secureflag is auto-detected based on whether CORS origins use HTTPS. If you switch from HTTP to HTTPS, existing sessions will be invalidated — users will need to log in again.
- Config backups: Encrypted at rest via OpenBao Transit envelope encryption before database storage.
- Audit logs: Encrypted at rest via Transit encryption — audit log content is protected even from database administrators.
- Subresource Integrity (SRI): SHA-384 hashes on JavaScript bundles prevent tampering with frontend code.
- Content Security Policy (CSP): Strict CSP headers prevent XSS, code injection, and unauthorized resource loading.
- No external dependencies: Fully self-hosted with no external analytics, telemetry, CDNs, or third-party services. The only outbound connections are:
- RouterOS firmware update checks (no device data sent)
- SMTP for email notifications (if configured)
- Webhooks for alerts (if configured)
The following security headers are enforced on all responses:
| Header | Value | Purpose |
|---|---|---|
Strict-Transport-Security |
max-age=31536000; includeSubDomains |
Force HTTPS connections |
X-Content-Type-Options |
nosniff |
Prevent MIME-type sniffing |
X-Frame-Options |
DENY |
Prevent clickjacking via iframes |
Content-Security-Policy |
Strict policy | Prevent XSS and code injection |
Referrer-Policy |
strict-origin-when-cross-origin |
Limit referrer information leakage |
- Immutable audit log: All significant actions are recorded in the
audit_logstable — logins, configuration changes, device operations, admin actions. - Fire-and-forget logging: The
log_action()function records audit events asynchronously without blocking the main request. Each call opens a dedicated database session and self-commits, ensuring audit entries are persisted regardless of whether the caller's transaction commits or rolls back. - Per-tenant access: Tenants can only view their own audit logs (enforced by RLS).
- Encryption at rest: Audit log content is encrypted via OpenBao Transit.
- CSV export: Audit logs can be exported in CSV format for compliance and reporting.
- Account deletion: When a user deletes their account, audit log entries are anonymized (PII removed) but the action records are retained for security compliance.
| Data Type | Retention | Notes |
|---|---|---|
| User accounts | Until deleted | Users can self-delete from Settings |
| Device metrics | 90 days | Purged by TimescaleDB retention policy |
| Configuration backups | Indefinite | Stored in git repositories on your server |
| Audit logs | Indefinite | Anonymized on account deletion |
| API keys | Until revoked | Cascade-deleted with user account |
| Encrypted key material | Until user deleted | Cascade-deleted with user account |
| Session data (Redis) | 15 min / 7 days | Auto-expiring access/refresh tokens |
| Password reset tokens | 30 minutes | Auto-expire |
| SRP session state | Short-lived | Auto-expire in Redis |
TOD provides built-in tools for GDPR compliance:
- Right of Access (Art. 15): Users can view their account information on the Settings page.
- Right to Data Portability (Art. 20): Users can export all personal data in JSON format from Settings.
- Right to Erasure (Art. 17): Users can permanently delete their account and all associated data. Audit logs are anonymized (PII removed) with a deletion receipt generated for compliance verification.
- Right to Rectification (Art. 16): Account information can be updated by the tenant administrator.
As a self-hosted application, the deployment operator is the data controller and is responsible for compliance with applicable data protection laws.