Architecture

Mezite is built around three core components: the Auth Service, the Proxy Service, and Agents. Together they provide authenticated, authorized, and audited SSH access to infrastructure without exposing backend servers directly to the network.

This page covers the system topology, component responsibilities, port layout, certificate trust model, SSH connection data flow, database schema, and deployment topologies.


System Overview

The diagram below shows how clients, the proxy, the auth service, agents, and target SSH servers relate to each other. In a combined-mode deployment (the default), mezhub runs both Auth and Proxy in a single process.

 ┌──────────────┐         HTTPS :3080          ┌──────────────────────────────────┐
 │              │ ──────────────────────────▶   │            mezhub               │
 │    msh CLI   │         SSH :3023             │  ┌────────────┐ ┌────────────┐  │
 │    Web UI    │ ──────────────────────────▶   │  │   Proxy    │ │    Auth    │  │
 │              │                               │  │  Service   │ │  Service   │  │
 └──────────────┘                               │  │            │ │            │  │
                                                │  │ :3080 HTTPS│ │ :3025 gRPC │  │
                                                │  │ :3023 SSH  │ │            │  │
                                                │  │ :3024 Tun  │ │  User CA   │  │
                                                │  │            │ │  Host CA   │  │
                                                │  │            │ │  Sessions  │  │
                                                │  └─────┬──────┘ └──────┬─────┘  │
                                                │        │    gRPC :3025 │        │
                                                │        └───────┬───────┘        │
                                                └────────────────┼────────────────┘
                                                                 │
                                                          ┌──────┴──────┐
                                                          │ PostgreSQL  │
                                                          │    :5432    │
                                                          └──────┬──────┘
                                                                 │
                      ┌──────────────────────────────────────────┘
                      │
       Reverse Tunnel :3024
                      │
        ┌────────────────────────────┐       ┌─────────────────────────┐
        │       mezd                 │       │      mezd               │
        │       (node-01)            │       │      (node-02)          │
        │                            │       │                         │
        │       SSH server           │       │       SSH server        │
        └────────────────────────────┘       └─────────────────────────┘

Components

Auth Service

The Auth service is the cluster's brain. It runs as a gRPC server on port 3025 and is responsible for:

  • User authentication — Verifying passwords (with optional TOTP), validating OIDC tokens, SAML assertions, LDAP credentials, and GitHub OAuth tokens, and issuing short-lived SSH certificates.
  • Certificate authorities — Managing the User CA (signs user certificates) and Host CA (signs host certificates). Both use Ed25519 keys. CA private keys are stored encrypted in the database.
  • RBAC enforcement — Evaluating role bindings against requested actions. Uses label-based matching with deny-overrides-allow semantics and template variables.
  • Node registration — Accepting join tokens from agents, issuing host certificates, and maintaining the inventory of registered nodes.
  • Session management — Tracking active SSH sessions, enforcing session TTLs, and storing session recordings.
  • Audit event storage — Writing structured audit events to PostgreSQL for every authentication, authorization decision, session start/end, and administrative action.

Proxy Service

The Proxy service is the public-facing entry point. It terminates all client connections and routes them to the appropriate agent. It exposes three listeners:

  • HTTPS (:3080) — Web UI, REST API, WebSocket session channels, and OIDC callback endpoints.
  • SSH (:3023) — Native SSH protocol. Clients connecting via msh ssh land here. The proxy authenticates the user's certificate, resolves the target node, and forwards the connection through the agent's reverse tunnel.
  • Tunnel (:3024) — Persistent reverse-tunnel connections from agents. Each agent maintains a gRPC stream to this port; the proxy multiplexes client connections over these tunnels.

Agent (mezd)

Agents run on every target machine. They register with the Auth service using a one-time join token, receive a host certificate, and establish a persistent reverse tunnel to the Proxy. Key responsibilities:

  • Reverse tunnel — Maintains a persistent outbound connection to the proxy's tunnel port (:3024). This means agents do not need any inbound firewall rules.
  • SSH server — Runs an SSH server that accepts connections forwarded through the tunnel. Verifies user certificates against the User CA.
  • Session recording — Captures terminal I/O at the PTY level after SSH decryption. The agent sees the clean text that the user sees in their terminal, not encrypted SSH protocol bytes. Recordings are streamed to the Auth service in real-time (node-sync mode) or uploaded after the session ends (node mode).

Certificate Authorities

Mezite operates two certificate authorities, both managed by the Auth service:

  • User CA (Ed25519) — Issues short-lived SSH certificates to authenticated users. These certificates encode the user's identity, roles, and permitted principals.
  • Host CA (Ed25519) — Issues host certificates to agents during registration. These certificates identify the host to connecting users, preventing MITM attacks.

CA private keys are stored in the certificate_authorities table in PostgreSQL, optionally encrypted with the ca_key_passphrase setting.


Port Reference

PortProtocolComponentDescription
3025gRPCAuth ServiceInternal auth API. Used by proxies, agents, and admin CLI. Should not be exposed publicly.
3080HTTPSProxy ServiceWeb UI, REST API, OIDC callbacks. The primary public endpoint.
3023SSHProxy ServiceSSH client connections. Clients connect here via msh.
3024gRPCProxy ServiceAgent reverse-tunnel connections. Agents connect outbound to this port.
5432PostgreSQLDatabaseMezite's state store. Only auth and admin tools connect here.

SSH Connection Data Flow

When a user runs msh ssh --login=user node-01, the following sequence occurs:

SSH connection flow — step by step text
1. msh opens an SSH connection to Proxy :3023
2. Proxy verifies the user's SSH certificate (signed by User CA)
3. Proxy evaluates RBAC: does this user's role allow login "user" on node-01?
4. Proxy looks up node-01 in the Auth service node registry
5. Proxy resolves node-01 to the agent's reverse tunnel
6. Proxy forwards the SSH channel through the tunnel to the agent
7. Agent verifies the user certificate against the User CA public key
8. Agent starts an SSH session as the requested login user
9. Agent allocates a PTY and records terminal I/O at the PTY level
10. Agent streams recording chunks to Auth via gRPC in real-time (node-sync mode)
11. Agent streams terminal I/O back through the tunnel to the client
12. Auth stores the recording and writes audit events: session.start, session.end

Certificate Trust Model

Mezite uses a mutual SSH certificate trust model. Both users and hosts present certificates signed by trusted CAs. This eliminates the need for password-based SSH, shared keys, or authorized_keys files.

 ┌─────────────────────────────────────────────────────────┐
 │                    Auth Service                         │
 │                                                         │
 │   ┌──────────────┐              ┌──────────────┐        │
 │   │   User CA    │              │   Host CA    │        │
 │   │  (Ed25519)   │              │  (Ed25519)   │        │
 │   └──────┬───────┘              └──────┬───────┘        │
 │          │                             │                │
 └──────────┼─────────────────────────────┼────────────────┘
            │                             │
            ▼                             ▼
   ┌────────────────┐            ┌────────────────┐
   │ User Certs     │            │ Host Certs     │
   │                │            │                │
   │ identity: bob  │            │ host: node-01  │
   │ roles: [admin] │            │ cluster: prod  │
   │ principals:    │            │ valid: 24h     │
   │   [root, bob]  │            │                │
   │ valid: 12h     │            │                │
   └────────────────┘            └────────────────┘
          │                             │
          │ presented to                │ presented to
          ▼                             ▼
   ┌──────────────┐             ┌──────────────┐
   │ Agent (host) │             │  msh (user)  │
   │ trusts       │             │  trusts      │
   │ User CA      │             │  Host CA     │
   └──────────────┘             └──────────────┘

The trust chain works as follows:

  1. User login — When a user authenticates (password+TOTP, OIDC, SAML, LDAP, or GitHub OAuth), the Auth service issues a short-lived SSH certificate signed by the User CA. The certificate encodes the user's name, roles, and allowed principals.
  2. Host registration — When an agent joins the cluster with a valid token, the Auth service issues a host certificate signed by the Host CA. The certificate encodes the node's hostname and cluster membership.
  3. Mutual verification — During an SSH connection, the agent verifies the user's certificate against the User CA public key, and the user's client verifies the host's certificate against the Host CA public key. Both sides reject connections if the certificate is expired, revoked, or signed by an unknown CA.

Database Schema

All cluster state is stored in the database (SQLite or PostgreSQL). The schema is managed by mezhub using golang-migrate and auto-migrates on startup. Below are the primary tables and their purpose.

TableDescriptionKey Columns
usersUser accounts. Stores credentials (bcrypt hashes for local auth), roles, and metadata.id, username, password_hash, roles, created_at, updated_at
rolesRBAC role definitions. Each role specifies allowed logins, node labels, and permissions.id, name, allowed_logins, node_labels, max_session_ttl
certificate_authoritiesCA keypairs (User CA and Host CA). Private keys are optionally encrypted.id, type, public_key, private_key, cluster_name, created_at
nodesRegistered SSH nodes. Updated by agents on heartbeat.id, hostname, addr, labels, last_heartbeat, tunnel_id
sessionsActive and completed SSH sessions.id, type, user, node_id, started_at, ended_at, recording_path
audit_eventsImmutable audit log. Every authentication, authorization decision, session lifecycle event, and administrative action is recorded here.id, event_type, user, resource, action, status, metadata, created_at
access_requestsJust-in-time access requests.id, user, requested_roles, reason, state, resolved_by, created_at, resolved_at, expires_at
auth_connectorsSSO connector configurations (OIDC, SAML).id, type, name, config, created_at

Migration files live in server/migrate/migrations/ and follow the NNN_description.up.sql / NNN_description.down.sql naming convention. Migrations are applied automatically when mezhub starts.


Deployment Topologies

Combined Mode (default)

A single mezhub process runs both Auth and Proxy services. This is the simplest deployment and works well for small to medium clusters (up to ~500 nodes).

Combined mode topology text
┌───────────────────────────┐
│         mezhub            │
│  Auth :3025 + Proxy :3080  │
│         + SSH :3023        │
│         + Tunnel :3024     │
└─────────────┬─────────────┘
              │
         PostgreSQL :5432

Separated Mode

For larger deployments or when you need independent scaling, run Auth and Proxy as separate processes (or containers). Multiple proxy instances can share a single Auth backend.

Separated mode topology text
                        ┌─────────────────┐
                        │  Load Balancer   │
                        └────────┬────────┘
                 ┌───────────────┼───────────────┐
                 ▼               ▼               ▼
          ┌────────────┐ ┌────────────┐ ┌────────────┐
          │  Proxy #1  │ │  Proxy #2  │ │  Proxy #3  │
          │ :3080 :3023│ │ :3080 :3023│ │ :3080 :3023│
          │ :3024      │ │ :3024      │ │ :3024      │
          └──────┬─────┘ └──────┬─────┘ └──────┬─────┘
                 │               │               │
                 └───────────────┼───────────────┘
                                 │  gRPC :3025
                 ┌───────────────┼───────────────┐
                 ▼               ▼               ▼
          ┌────────────┐ ┌────────────┐
          │   Auth #1  │ │   Auth #2  │  (active/standby)
          └──────┬─────┘ └──────┬─────┘
                 │               │
                 └───────┬───────┘
                         ▼
                    PostgreSQL
                  (primary + replica)

In separated mode, each proxy handles SSH connections (:3023), HTTPS traffic (:3080), and agent tunnels (:3024). All proxies connect to the Auth service over gRPC (:3025) for authentication and authorization decisions.


Next Steps