You have a Linux server. You can SSH into it. You can stare at its logs until the letters begin to rearrange themselves into career advice.
What you would prefer is a small, understandable setup: send commands from Phoenix, see the agents and their logs, and get a Telegram message when someone successfully logs in over SSH.
A reasonable request. Naturally, we have brought a Railgun.
This walkthrough builds that setup from the beginning: install MatrixOS, create a Phoenix Swarm Workspace, assign its connections, deploy it through Railgun, and verify the complete path with a controlled login.
The five agents we add are matrix_https, matrix_websocket, log_streamer, telegram_relay, and gatekeeper. Phoenix creates the matrix root for us, so the finished tree contains six nodes.
A naming note before anyone searches the palette for twenty minutes: the HTTP command-ingress agent is named matrix_https in the current catalog, and the Telegram agent is telegram_relay. Those are the names used below.
The mission map
| Component | Responsibility |
|---|---|
| Phoenix | The operator application: vault, Registry, workspace editor, and live cockpit |
| MatrixOS | The runtime installed on the Linux target |
| Railgun | Installs the runtime and launches deployments through SSH |
matrix |
Starts the swarm, establishes its chain of trust, and routes commands |
matrix_https |
Receives authenticated Phoenix commands over HTTPS |
matrix_websocket |
Provides the live return connection for replies, alerts, and session traffic |
log_streamer |
Supplies agent runtime logs to Phoenix |
gatekeeper |
Watches authentication-log entries for successful SSH logins |
telegram_relay |
Sends alert messages to the configured Telegram chat |
Here is the traffic flow. These arrows describe communication, not the parent-child layout of the workspace:
INSTALL / LAUNCH
Phoenix → Railgun → SSH → MatrixOS / MatrixD
LIVE CONTROL
Phoenix → HTTPS ingress → Matrix → requested agent
Agent reply / logs → WebSocket relay → Phoenix
LOGIN NOTIFICATION
SSH authentication log → Gatekeeper → hive.alert → Telegram relay → your chat
└──→ WebSocket relay → Phoenix feed
SSH is the installation and launch route. HTTPS and WebSocket are the live cockpit routes. Successfully connecting by SSH does not prove the cockpit ports are reachable.
Telegram is an alert destination here. Typing “restart everything” into the bot chat is not part of this agent’s command interface, which is probably good news for everyone in the group.
1. Prepare the target and open your vault
Use a Linux machine you administer, preferably a lab VM for the first run. The current installer supports provisioning through apt-get or dnf. It provisions the Python 3.12 runtime it needs, including a verified source-build fallback when the required runtime is unavailable from the host’s packages.
Have these ready:
- Phoenix with an unlocked encrypted vault;
- an SSH account on the target, with root access or the passwordless-sudo access required by Railgun’s provisioning steps;
- the target’s independently verified SSH host-key fingerprint;
- an address Phoenix can reach, two available non-privileged TCP ports, and matching network/firewall access;
- a Telegram bot token and destination chat ID;
- a real authentication log that records SSH events.
The runtime service account is a separate matter from the privileged SSH account. Railgun uses provisioning privileges to prepare the host, then runs the swarm under its dedicated non-root Linux user.
For the examples, we will use:
| Example item | Value |
|---|---|
| Target reachable address | 10.20.30.40 |
| Phoenix workstation’s source address | 10.20.30.10 |
| HTTPS command port | 8443 |
| WebSocket return port | 8765 |
| Universe | first-swarm |
| Swarm Linux user | matrix-first-swarm |
These are example values, not a server waiting for you. Replace them with your network’s actual values. If NAT or a VPN changes the source address seen by the target, use that observed source when configuring allowlists.
Permit the two cockpit ports from the operator’s intended address or network, and keep SSH access available for installation. The target also needs outbound access for installation dependencies and Telegram delivery. A new universe on the same server needs available ports too; a different name does not let two processes own the same socket.
2. Put SSH in the Registry
Open Registry, create an SSH record, and give it a recognizable label, such as lab-server-ssh.
In its Connection tab, enter Host, Port, Username, and Trusted Fingerprint. In Authentication & Keys, select the authentication method you actually use and enter the corresponding existing credentials. An encrypted private key needs its own passphrase; that is different from the server account’s password.
Use Test Connection, then Save to Vault. Establish the host fingerprint from a trusted source, such as the server console or your provisioning inventory. Treat a fingerprint mismatch as something to investigate rather than a speed bump to flatten.
The SSH record tells Railgun which machine it may contact. It does not configure the HTTPS or WebSocket agents. Those get their own records shortly.
Credentials belong in the vault. A screenshot of a private key is a surprisingly efficient way to turn documentation into an incident.
3. Install MatrixOS with Railgun
Open Railgun → Check Remote Host…, select the SSH target, and run Run Full Check. Review the results for SSH access, OS, Python, disk space, system clock, /matrix, and an existing installation. This is a useful preflight, not a guarantee that every application-level requirement has been satisfied.
If MatrixOS is already installed and current enough for this Phoenix build, proceed to the workspace steps. Otherwise open Railgun → Install MatrixOS….
Choose the source
| Install Mode | What it uses |
|---|---|
| Install from GitHub | The project’s GitHub repository, fetched by the remote installer |
| Local Full Install | Your selected local MatrixOS source folder, uploaded to the target |
For Local Full Install, Local Source Path should point to the MatrixOS root, the directory containing its runtime source such as agents, core, and scripts. In the monorepo, this is the matrixos directory—not the Phoenix application directory or a Python virtual environment.
Use matching Phoenix and MatrixOS versions. A local UI describing features that an older remote runtime has never met is not a compatibility strategy.
Choose the environment and target
For a fresh installation, select Create new venv. Skip Python setup is for an already suitable MatrixOS environment; it is not the beginner’s “make this faster” button.
Select the saved SSH target under Deploy To, then click Install MatrixOS.
The installer places MatrixOS under /matrix, creates or uses its isolated environment at /matrix/.venv, installs dependencies, and prepares the matrixd launcher. Watch the output and wait for Installation complete. An upload reaching 100% is only an upload milestone; dependency installation and validation can still be running.
If Python must be built, allow that stage to finish. The progress window has not entered a reflective phase. Compilers simply have hobbies.
Installing MatrixOS prepares the runtime. It does not deploy this swarm yet.
4. Create the three application connection records
In Registry, create an HTTPS record, a WSS record, and a Telegram record. You can also create or select these from the workspace’s requirement-assignment controls; preparing them now makes the later steps easy to follow.
HTTPS: Phoenix sends commands
For the HTTPS record:
| Field | Example |
|---|---|
| Label | first-swarm-https |
| Host |
10.20.30.40, without a URL scheme or path |
| Port | 8443 |
| Allowlist IPs | The operator source IP actually seen by the target |
| Channel | outgoing.command |
| Primary Outgoing Transport | Enabled for this single-command-transport example |
| Directive Path | Keep config
|
Use an address Phoenix can reach. 0.0.0.0 is not a destination to enter here. The current agents listen on the target’s interfaces; the Registry Host supplies the address used to connect to them.
WSS: Phoenix receives live results
For the WSS record, use the same reachable host and port 8765. Set the appropriate source-IP allowlist, keep the channel payload.reception, enable Primary Payload Reception Transport, and keep Directive Path config.
Those channel names are from Phoenix’s perspective: commands go out, payloads come back. The WebSocket agent’s “egress” role describes traffic leaving the swarm toward Phoenix.
Leave the generated Serial values alone. Labels are for humans; identities and references are doing additional work behind the curtain.
Telegram: give the messenger an address
For a first exercise, a new dedicated bot and a private test chat are straightforward:
- Contact Telegram’s official BotFather, send
/newbot, and follow its prompts to obtain the bot token. - Open a private conversation with your new bot and send
/startor a message. A bot needs that initial user contact before ordinary private messages can be sent back. - Obtain that conversation’s numeric chat ID, then save the token and chat ID in the Telegram Registry record.
Telegram documents bot creation in its official tutorial. For a new bot with no webhook or other update consumer, you can retrieve an incoming update through getUpdates and read message.chat.id from your test message. Use a local API client and supply the token privately; do not put a real token into a tutorial, a shared screenshot, or a browser address you plan to share. If a webhook is already configured, getUpdates is unavailable—use the existing integration’s received update instead.
The Registry fields are Label, Chat ID, and Bot Token. Keep the Telegram channel selection and Directive Path config/telegram. The token is the bot credential; the chat ID is the destination. They are not interchangeable, despite both looking like something a database coughed up.
For a group destination, add the bot to that group, use the group’s actual chat ID, and ensure the bot can send there. Verify the destination before connecting real login notifications to it.
5. Build the workspace tree
Click Deploy in Phoenix’s main toolbar. This opens Swarm Workspaces. Choose New for this tutorial, or Open an existing workspace you intend to extend. The manager also offers Clone when you want a separate editable copy.
A new workspace starts with the matrix root. In the Agent picker, search for each of the five agents and drag it onto empty canvas space or directly onto Matrix. For this example, all five should be direct children of the root:
matrix
├── matrix_https
├── matrix_websocket
├── log_streamer
├── telegram_relay
└── gatekeeper
Drop onto another agent and you can create a different parent relationship. If that happens unintentionally, right-click the misplaced node and use Reparent To → matrix.
This is the agent hierarchy. You do not need to put Telegram underneath Gatekeeper to route its alerts. The service roles supply that connection.
Select a node to inspect it on the left. Double-click a configurable agent to open its configuration editor. If the picker appears to have lost an agent, use Clear filters before filing a missing-person report.
The workspace saves changes to the vault. Watch for Saved to Vault ✓; a save failure has a Retry Save action. Do not close an editor that is still telling you its changes have not been saved. You can rename the workspace in the manager to something recognizable, such as First Swarm Lab.
6. Satisfy the requirements without collecting loose certificates
Select each node and look at Agent Inspector → Requirements.
| Node | What you should see or assign |
|---|---|
matrix |
Automatic packet-signing material |
matrix_https |
Automatic signing and connection certificates; assign the HTTPS Registry record |
matrix_websocket |
Automatic signing and connection certificates; assign the WSS record |
log_streamer |
Automatic packet-signing material |
telegram_relay |
Automatic packet-signing material; assign the Telegram record |
gatekeeper |
No manual Registry requirement in the current palette entry; configure its behavior separately |
AUTOGEN rows are generated during compilation. They are not missing credentials that you must hunt down on the filesystem. For assignable rows, use Assign or change, select the correct Registry record, and confirm that the row is satisfied.
Leave the supplied signing and certificate requirements intact. The deployment compiles the corresponding agent and Phoenix-side material, including the connection certificates used for mutual TLS. A normal browser visiting the port is not the intended client and may be rejected because it lacks that client identity.
Missing required assignments stop deployment. This is helpful. Discovering an absent Telegram token before launch is considerably less dramatic than discovering it during an incident.
7. Configure the agents and keep their service roles
For this first swarm, most defaults are useful. The important settings are the ones that let the agents find each other and do their actual jobs.
HTTPS and WebSocket
Double-click each transport agent and leave Enable Lockdown (Disable Packet Processing) unchecked for this exercise. Host, port, and allowlist settings come from the Registry records assigned above.
Keep the WebSocket relay’s supplied roles, including hive.rpc and hive.alert. These let it carry replies and receive alerts. Removing roles to make the configuration look tidier is how a perfectly healthy connection becomes a remarkably quiet one.
Log streamer
Keep log_streamer and its hive.log_streamer service role. Phoenix requests an agent’s runtime log through this service when you select that agent in the live tree.
It supplies visibility into the swarm. Gatekeeper itself reads the operating system’s SSH authentication log; log_streamer does not take over that job.
Telegram relay
Open telegram_relay and keep Enable Telegram alert delivery enabled. Leave Encrypt Telegram alert message off for the initial delivery test; we will cover that option below.
Keep its hive.alert role. Gatekeeper sends to that role, and the relay publishes the message to the chat configured in its Registry record. WebSocket also advertises the alert role, so a connected Phoenix session can receive the event too.
No hand-written destination UUID is needed for this basic arrangement.
Gatekeeper
Start with these settings:
| Setting | First-run choice |
|---|---|
| Alert Role | hive.alert |
| Ignore IPs | Empty while testing |
| Log Path | The target’s real SSH authentication log; read the path-selection note below |
| MaxMind DB | An existing GeoLite2 City database, if you want location enrichment |
Gatekeeper follows new log lines with tail -n 0 -F. It recognizes successful OpenSSH-style entries containing Accepted and from, extracts the login account and source address, and sends an alert. It does not replay yesterday’s log when it starts.
The name sounds like a bouncer. In this setup, its job is to notice who came through the door and call the operator. It does not authenticate SSH users, block a login, ban addresses, or turn a successful login into proof of compromise.
Path selection matters: the current implementation prefers /var/log/secure when that file exists, then /var/log/auth.log, and only then the configured fallback. Confirm the path reported in its startup log. On a host that only keeps authentication events in the journal, configure the system logger to produce a suitable text log; creating an empty file does not persuade SSH to write into it.
Railgun derives log-access needs from the swarm and provisions the runtime account accordingly. Check its permission output. Initial access to an existing file is only part of the job: the account must still be able to read new files after log rotation. The current provisioning includes a persistent rule for the supported secure/rsyslog path; auth.log rotation may need distro-specific log-creation or ACL configuration. Verify that on your host rather than making authentication logs world-readable.
Optional MaxMind data adds a location estimate. Missing location data does not make a successful login disappear. An IP-derived city is context, not a sworn witness.
A current-build detail: the editor exposes Enable GeoIP and Always Alert, but the successful-login path does not currently use those switches to gate its lookup or apply the cooldown helper. Do not rely on those switches to suppress lookups or throttle this flow. Ignore IPs is an exact-address exclusion list; leave it empty until the test works, and add only intentional exclusions afterward.
Gatekeeper also contacts api.ipify.org to obtain the server’s public IP while constructing alerts. That outbound dependency can delay processing if it stalls. This walkthrough promises a verifiable notification path, not a fictional millisecond delivery guarantee.
8. Deploy the workspace through Railgun
Your workspace is the editable recipe in the vault. A deployment compiles that recipe, resolves its Registry references, generates the necessary identities, and launches an instance on the target.
Click Deploy inside the workspace. Resolve any missing requirements before proceeding.
Give the deployment a recognizable Deployment Label, then review Directive Deployment Options:
| Option | Choice for this first deployment |
|---|---|
| SSH Target | The tested SSH Registry record |
| Universe Name |
first-swarm, or another unused simple name |
| Swarm Linux User | A dedicated non-root account, such as matrix-first-swarm
|
| Embed agent sources (Clown Car) | Leave off when using the matching installed MatrixOS runtime |
| View the fully resolved directive after dependency to agent mappings | Keep enabled so you can review the result |
--protect-memory |
Keep enabled |
--debug, --rug-pull, --clean
|
Leave off for this tutorial |
The label is for your eyes. The universe is an operational identifier: use a simple value without spaces, and check it explicitly rather than accepting a long display label copied into the field.
Review --reboot: it is enabled by default in the current dialog. Deploying to an existing universe is an operational change, not a harmless preview. Use a new universe and free ports for this exercise. Do not select every flag because they look lonely.
The optional Clown Car mode embeds agent source into the deployment. It does not replace installing MatrixOS, Python, or dependencies. For the beginner path here, the installed runtime supplies the agents.
There is also an option to remove previous deployment records after success for the same universe and target IP. That controls saved deployment records; it is not permission to clean arbitrary files on the server. Review it if you want to retain older records.
Read the staging view, then launch
With the preview option selected, Phoenix opens Directive Encryption Staging. Check the agent tree, target addresses, ports, alert role, and resolved assignments.
The resolved view can contain secrets. Review it locally; do not paste the entire thing into a support thread. “Please find attached our credentials” is not a diagnostic format.
Encrypt & Save commits this deployment and continues into the Railgun launch flow. It is the launch action for this workflow, not a button for saving an unfinished workspace.
What Railgun does
Railgun checks the pinned SSH target and the remote MatrixD launch capability, prepares the dedicated runtime user and agent-specific host access, and sends a sealed boot envelope over SSH standard input. MatrixD receives and processes that envelope to start the selected universe.
The sealed launch path avoids staging the plaintext directive and key as ordinary launch files or putting them in command-line arguments. That does not make the entire server diskless: the installed runtime, logs, and normal agent state still exist. Memory protection also does not make the process inaccessible to root.
Watch the Railgun output for completion and a successful exit. A saved deployment record alone is insufficient evidence—the record can exist before remote launch is confirmed.
If the SSH session times out or you cancel the local operation, inspect the target and the Swarms view before launching again. Stopping the local wait does not prove that the remote process stopped. Distributed systems remain committed to making “did it happen?” a full-time occupation.
9. Connect Phoenix and prove the whole route
Use Connect and select the deployment you just launched. Phoenix uses that deployment’s transport records and generated credentials for the HTTPS/WSS session.
Work through these checks in order:
- Live tree: confirm that the root and five child agents appear and report current activity.
- Agent logs: select Gatekeeper in the live tree and inspect Agent Logs. The selection requests the log stream. Look for its actual authentication-log path and any read errors.
- Relay health: select the Telegram relay and check that delivery is enabled and there are no missing-configuration or API errors.
- Fresh login: keep your existing administrative session open and make one new, authorized SSH login from an address absent from Gatekeeper’s ignore list. Do this after Gatekeeper has started following the log.
- Source event: confirm that the target recorded the corresponding successful OpenSSH authentication entry.
- Delivery: look for the event in the Phoenix alert feed and the intended Telegram chat. The relay’s successful-send log is useful supporting evidence; check the actual destination too.
The notification should identify the login account, source IP, authentication method, and recorded time, with location information when available. Some fields, such as terminal details, may be unknown. That is a better answer than confident fiction wearing a dashboard badge.
Do not test this by attacking your own password prompt. This tutorial exercises the successful-login flow. An ordinary authorized login is sufficient.
Once the message arrives, you have demonstrated the complete chain:
real SSH login
→ real authentication entry
→ Gatekeeper
→ hive.alert
→ Telegram relay
→ the right chat
You also have a separate way to observe the agents through Phoenix and log_streamer. That makes the next failure diagnosable instead of merely atmospheric.
10. Optional: encrypt the Telegram alert body
After ordinary delivery works, enable Encrypt Telegram alert message in the relay’s configuration and deploy the updated workspace. Workspace edits affect the next deployment; saving the recipe does not silently rewrite a running swarm.
With encryption enabled, the relay signs and encrypts the alert body using the deployment’s assigned packet keys. Telegram receives a wrapped message beginning with:
MATRIXSWARM-ENCRYPTED-ALERT-V1
It will look less like a login notification and more like a keyboard falling down a staircase. That is expected.
In Phoenix, open the Telegram relay’s panel, paste the complete wrapped alert, and use Decrypt with the matching deployment credentials. Phoenix verifies and displays the recovered message. Retain the appropriate deployment material if you need to read alerts from older deployments.
If the relay cannot create the encrypted wrapper, it blocks that send rather than falling back to plaintext. Check its logs when testing.
This option protects the alert body. Telegram still handles the bot/chat relationship and delivery metadata. The wrapper’s expiry information is not a Telegram self-destruct timer; Phoenix can mark an expired message as view-only, and the chat message is not automatically deleted by that setting.
11. When the server declines to text back
| Symptom | Check first |
|---|---|
| MatrixOS installation failed | Actual installer output, supported package manager, provisioning privileges, network access, and disk space |
| Workspace refuses deployment | Unassigned Registry requirements and invalid deployment fields |
| SSH works, Phoenix does not connect | Reachable Registry Host, both cockpit ports, firewall rules, actual source-IP allowlists, correct deployment credentials, and transport lockdown settings |
| Agents appear, logs do not |
log_streamer, its hive.log_streamer role, and the WebSocket return path |
| Gatekeeper is alive but quiet | Its selected log path, runtime-account read access, a fresh Accepted entry, and exact IP exclusions |
| Alerts stop after log rotation | Permissions on newly created authentication logs and persistent logging/rotation configuration |
| Gatekeeper processing stalls | Authentication-log following, optional lookup errors, and outbound access to api.ipify.org
|
| Telegram has nothing | Bot token, correct chat ID, initial user contact, send permissions, delivery enabled, shared hive.alert role, and outbound Telegram API access |
| Encrypted Telegram text will not open | Complete wrapper, matching deployment keys, verification status, and relay encryption logs |
Check one hop at a time. Reinstalling everything because a chat ID is wrong is technically activity, but so is running in circles with a clipboard.
The workflow you can reuse
The pattern for the next swarm is the same:
Install the runtime once → create or clone a workspace → add agents → assign Registry requirements → configure behavior → deploy → connect → test a real event.
Keep the small transport-and-logging foundation when you want a live Phoenix cockpit. Add the next useful worker beneath Matrix, configure its service roles and requirements, and verify its output. Telegram can carry other agents’ compatible alerts too; Gatekeeper is simply the first producer in this exercise.
You now have a swarm you can command, observe, and hear from when someone successfully logs in. Six nodes. Five added agents. One server that finally answers its messages.
Try the controlled login test, then tell us which agent you would add next. The server has completed basic communication training. We should probably use this opportunity before it discovers meetings.
Victory Always!
🌐 Links & Resources:
Try MatrixSwarm: https://matrixswarm.com
Join the Community / Discord: https://discord.gg/2USbWVBVV
Download Server: https://github.com/matrixswarm/matrixswarm
Youtube: https://www.youtube.com/channel/UCMjiY4_-W2KP5fHXO0eC2ug
Top comments (1)
This walkthrough takes a reader from bare SSH to a live five-agent swarm that texts you on every successful login, which is the clearest proof a framework can give that it actually works. Looking forward to the next one.