Remotely
One platform. Every client. Complete control. A self-hostable access, monitoring and compliance console for managed service providers.
Overview
Remotely is a self-hostable privileged-access and infrastructure-operations platform for MSPs and IT service companies that manage infrastructure for many clients.
Those teams usually juggle five or six separate tools: remote access, privileged access management, monitoring, compliance evidence, session recording and ChatOps. Remotely puts them behind one browser console, with each client as an isolated tenant.
By the numbers
Lines of code by part: control plane ~14k, web ~18.5k, agent ~1.7k, Terraform provider ~1.4k, CLI and SDK ~600.
The problem
- MSP engineers need access to hundreds of servers, databases and clusters across many client networks, each with its own VPN, credentials and tooling.
- Enterprise PAM tools are expensive and complex, and none were designed for multi-tenant MSPs.
- Open-source options such as Teleport Community, Apache Guacamole and JumpServer each cover a slice. None combines access, live infrastructure visibility, monitoring and compliance evidence in one multi-tenant model.
What I built
Seven deliverables:
- Control plane: Node.js, Express and WebSockets. Auth, RBAC, session brokering, recording, audit, discovery, monitoring and compliance.
- Web console: React 19 and Vite, 41 pages.
- Lightweight agent: installed on client machines. It dials out to the control plane, so client networks need no inbound ports. Ships as a single executable for Linux and Windows and updates itself.
- CLI:
remotely login / ls / sshfrom a terminal. - TypeScript SDK:
@remotely/sdk. - Terraform provider: written in Go; manages roles, users, organisations and connections as code.
- One-command demo environment: Docker Compose with real SSH, RDP, VNC and Postgres targets plus a Dex OIDC identity provider.
Key features
Secure access, all in the browser
- SSH through an agent reverse tunnel (the Teleport model) or by direct dial from the control plane.
- Just-in-time SSH keys: a fresh ephemeral keypair per session, authorised through sshd's
AuthorizedKeysCommand, the same model as AWS EC2 Instance Connect. No long-lived credential sits on the target. - RDP and VNC in the browser through Apache Guacamole's
guacd, with per-role clipboard control. - Database consoles for PostgreSQL, MySQL, MongoDB and Redis, with every query audited.
- Kubernetes pod exec and a cluster browser. It uses impersonation, so the cluster's own RBAC still applies.
- File transfer over SFTP, or a custom protocol over the agent tunnel.
Sessions and access control
- Recording and replay of SSH, RDP and database sessions; read-only live watching enforced by the server; moderated sessions that need a moderator to join first.
- RBAC with allow/deny labels, resource scoping, login allowlists, time limits, source-IP rules and time-bound roles.
- Delegated tenant admins who can manage their own client but cannot escalate or cross tenants.
- Just-in-time access requests approved from the console, Slack, Microsoft Teams, PagerDuty or Discord, plus break-glass access flagged separately in the audit log.
- Machine identities for automation, kept separate from human users.
Authentication and hardening
- OIDC single sign-on (authorization code with PKCE), TOTP and WebAuthn passkeys, and fully passwordless login.
- Rate limiting, lockout with alert emails, a password policy, and "log out everywhere" that revokes every issued token on the next request and WebSocket connection.
- Tamper-evident audit log: each entry is SHA-256 chained to the previous one, and a verify endpoint shows exactly where a chain breaks.
- SSRF protection on webhook destinations, strict security headers, and 60-second path-scoped download tokens.
Discovery, diagrams and operations
- Discovery from AWS, Azure, GCP, VMware vCenter and Docker/Podman hosts. AWS requests use hand-written SigV4 signing.
- Auto-generated architecture diagrams, plus a diagram editor built on React Flow with official cloud icons, multi-page diagrams, version history, live multi-user editing, share links and seven export formats.
- Blast-radius view: shows who can reach each resource and what a given access grant exposes.
- Infrastructure snapshots and diffs between two points in time.
- Built-in monitoring for small fleets (agents report every 15 seconds, with charts and threshold alerts), uptime monitors, signed webhook and SIEM export, and a SOC 2-style report built from live state that marks the controls it cannot verify.
- Multi-tenant layer: white-label branding, per-tenant usage and SLA metrics, per-plan feature flags and a platform super-admin.
Architecture
- Two access models side by side: an agent-based reverse tunnel with zero inbound ports, and agentless direct dial with just-in-time keys.
- Discovery is its own pipeline, separate from session access. It feeds the diagrams, snapshots and blast-radius view.
- Persistence uses SQLite through Node's built-in
node:sqlite. Session recordings are append-only, timestamped JSONL.
Engineering highlights
- Few dependencies by design. AWS SigV4, the OIDC flow (discovery, PKCE, JWKS verification) and TOTP are built on Node's
crypto. For WebAuthn, where the protocol surface is large and security-critical, I deliberately used@simplewebauthninstead of writing it myself. - Single-executable agent. Node SEA cannot
require()native modules from inside the binary. I patched node-pty's loader to useprocess.dlopenand built inside a Linux container so the native addon compiles for the real target. - Agent identity. A single-use join token bootstraps each agent's own ed25519 key. After that the agent reconnects by signing a challenge, so it never stores a shared secret.
- Verified against real systems, not mocks: real sshd, xrdp, x11vnc, Postgres and MySQL, a Dex identity provider, Chrome's virtual WebAuthn authenticator and a separate HMAC-verifying webhook receiver. Playwright runs caught bugs that type-checking missed.
Tech stack
- Backend Node.js 24, TypeScript, Express 4,
ws,ssh2,node-pty,@kubernetes/client-node,pg,mysql2,mongodb,redis,node:sqlite,@simplewebauthn/server,dagre - Frontend React 19, Vite 8, TypeScript,
@xterm/xterm,@xyflow/react(React Flow 12), Mermaid 11, Guacamole protocol client - Agent Node.js Single Executable Application (Linux x64 and Windows),
node-pty, self-update - IaC and SDK Terraform provider in Go (
terraform-plugin-framework), TypeScript SDK - Demo infra Docker Compose with
guacd, xrdp, x11vnc, OpenSSH, Postgres 16 and Dex OIDC
Challenges and lessons
- The
ssh2library does not support SSH certificates, so I chose just-in-time authorisation instead. It gives the same security property through a different mechanism. - Real-browser testing found bugs that unit-level checks never would: React StrictMode double-mounts that broke WebSocket state, dev-proxy prefix collisions, and a canvas focus issue that silently dropped RDP keyboard input.
- I wrote down what the platform can and cannot verify. For example, the compliance report refuses to "pass" encryption at rest when it cannot prove it.
Status and roadmap
Working proof of concept, demoed to prospective MSP clients. All core features work and were verified against real services.
Next steps toward production:
- Encrypt stored secrets at rest with managed keys.
- An automated test suite in CI.
- High availability with a Postgres backend.
- Short-lived certificate-based sessions.
- Signed multi-platform agent releases.