
Give AI agents real email inboxes while keeping the management API under your control
Sentio is a self-hosted email inbox API designed for applications and AI agents that need to receive messages, process structured email events, and send replies through a REST API. In this guide, we install the complete Sentio stack with Docker Compose, verify its local API on port 8080, address its bootstrap credentials, and cover essential operating and troubleshooting tasks. Once the service works locally, we connect it to an HTTP tunnel with Localtonet so an authorized remote agent can reach the REST API without inbound router port forwarding, firewall changes, a VPN, or a public IP address.
๐ What's in this guide
What Sentio provides for AI agents
Many agent workflows need more than a notification address. An agent may need to receive a customer conversation, inspect the authenticated and parsed message, take an action, and reply in the same thread. Building that flow from separate components usually means combining an SMTP server, message parser, spam and malware checks, storage, queues, an IMAP polling process or webhook layer, and an outbound delivery mechanism.
Sentio packages that path into a self-hosted, multi-tenant mail service. A deployment can create domains and mailboxes for agents, route inbound messages to application webhooks, and accept outbound messages through a bearer-authenticated REST API. Tenancy applies to domains, mailboxes, API keys, rate limits, suppression lists, and spam profiles, which is useful when one application serves multiple customers or separates multiple agent environments.
This guide concentrates on installing and remotely reaching the REST API. It does not attempt to turn an ordinary tunnel into a complete public mail-delivery architecture. Sentio exposes SMTP listeners as well, but production email requires additional DNS, certificate, reputation, and network planning that is separate from making an HTTP API available to an authorized agent.
/docs and an OpenAPI 3.1 description at /openapi.json.
The services behind the API
Sentio is not a single stateless HTTP binary when operated as a complete mail platform. Its documented Docker Compose stack starts Sentio together with PostgreSQL, Redis, NATS with JetStream, MinIO, ClamAV, and rspamd. These services cover persistent state, key-value operations, queues, object storage, malware scanning, and content analysis.
| Component | Role in the stack | Operational implication |
|---|---|---|
| PostgreSQL 18+ | Stores persistent application state | Version 18 is a hard requirement because the schema uses the built-in uuidv7() function. |
| Redis or Valkey | Supports rate limits, bans, greylisting, and reputation data | Sentio expects the key-value layer to remain reachable during operation. |
| NATS with JetStream | Runs delivery, retry, webhook, and event pipelines | JetStream must be enabled. A plain NATS deployment without it is insufficient. |
| S3-compatible storage | Stores raw email and attachment blobs | The Compose stack uses MinIO, while manual deployments can use another compatible endpoint. |
| ClamAV | Scans content for malware | Its startup and readiness can affect how quickly the complete stack becomes usable. |
| rspamd | Provides content-oriented spam analysis | It is included in the full Compose environment rather than needing a separate initial installation. |
Prerequisites and deployment choices
The most reproducible installation path is the official Docker Compose stack. It includes the supporting services and applies Sentio's migrations during startup, so this is the path used for the complete procedure below.
Prepare a host with Docker Engine 24 or newer, the Docker Compose plugin, approximately 4 GB of available memory, and approximately 8 GB of disk space. The published container image supports Linux on AMD64 and ARM64. Ensure that Git and curl are available as well, because we use them to retrieve the repository and verify the local API.
The Compose configuration publishes mail and API ports. Check whether another mail server, reverse proxy, development service, or management console already occupies ports 25, 465, 587, 8080, or 9001. A conflict prevents the affected container from binding successfully. Port 465 is skipped by Sentio when no certificates are present, but the remaining published ports still need careful review.
Docker Compose versus a manual installation
| Installation path | Best suited to | Requirements and tradeoffs |
|---|---|---|
| Prebuilt Compose stack | Most evaluations and self-hosted deployments | Pulls a prebuilt Sentio image and starts the documented PostgreSQL, Redis, NATS, MinIO, ClamAV, and rspamd dependencies. |
| Compose build from source | Developers changing Sentio itself | Builds the Rust workspace from the checkout and takes longer than using the prebuilt image. |
| Manual installation | Operators integrating with existing infrastructure | Requires a stable Rust toolchain, C toolchain, CMake, libclang-dev, PostgreSQL 18+, Redis or Valkey, JetStream-enabled NATS, and S3-compatible storage. |
A manual deployment gives an operator more control over service placement, but it also transfers responsibility for configuring every dependency, applying migrations, supplying a correct Sentio configuration, and defining a service manager unit. The supplied evidence confirms those requirements but does not provide enough complete configuration content here to reproduce every manual file safely. We therefore do not invent systemd units, environment variables, migration flags, or storage credentials. Use the Docker workflow below unless you have reviewed the exact manual configuration for the Sentio version you intend to deploy.
If you do select a manual deployment, confirm the database version before applying migrations:
psql -tAc 'SHOW server_version'
PostgreSQL 18 is a strict floor. On an older server, the migration fails because uuidv7() does not exist. Upgrading or selecting the correct PostgreSQL server is the proper resolution rather than trying to remove the function from Sentio's schema.
Install Sentio with Docker Compose

The following workflow uses the full official stack. Run it on the machine that will host Sentio and, later, the Localtonet client.
Clone the Sentio repository
Retrieve the repository and enter its directory. The Compose files and supporting configuration are maintained with the source tree.
git clone https://github.com/truespar/sentio.git
cd sentio
Choose whether to follow the current checkout or pin a release
For a reproducible deployment, check out a release that you have reviewed. The evidenced release is v0.1.6. Pinning avoids silently changing application code when the repository advances.
git checkout v0.1.6
Start the complete stack
Docker Compose pulls the prebuilt Sentio image and starts its required supporting services. The image is published for Linux AMD64 and ARM64, so this path does not compile the Rust workspace locally.
docker compose up -d
Watch the migration process
Sentio applies migrations before the server starts. Follow the migration service logs and look for a successful schema and bootstrap process rather than assuming that a running container means initialization finished.
docker compose logs -f sentio-migrate
Inspect the Sentio server logs
After migrations complete, follow the application logs. Exit log-following with your terminal's normal interrupt shortcut when the server is ready.
docker compose logs -f sentio
Building Sentio from the checkout
If you intentionally want to compile the complete workspace instead of pulling the published image, use the documented Compose build overlay:
docker compose -f docker-compose.yml -f docker-compose.build.yml up -d --build
This is a release build of the workspace and can take substantially longer than pulling the prebuilt image. It is useful when auditing or modifying code, but it is not required for the normal self-hosting path.
Understand the published ports
| Port | Protocol or service | Purpose |
|---|---|---|
| 25 | SMTP | Inbound email from other mail servers through MX delivery. |
| 465 | SMTPS | Implicit TLS. Sentio only binds this listener when certificates are present. |
| 587 | SMTP submission | Authenticated message submission using STARTTLS. |
| 8080 | HTTP REST API | Application API, health endpoint, interactive documentation, and OpenAPI description. |
| 9001 | MinIO console | Optional object-storage administration console that should not be exposed unnecessarily. |
The rest of this guide targets only port 8080. We do not send SMTP ports through the HTTP tunnel because SMTP is a different protocol and public mail delivery has additional requirements.
Verify Sentio locally before exposing it

A remote tunnel should be the final connectivity layer, not a substitute for local diagnosis. Confirm that Sentio and its dependencies work on the host first. This separates application failures from tunnel configuration errors.
Check container state
docker compose ps
Review every service, not only the main Sentio container. The API can start late or report an unhealthy dependency when PostgreSQL, Redis, NATS, storage, or a scanning service is still initializing. If a service is restarting, inspect its logs before proceeding.
Call the readiness endpoint
Sentio documents /health/ready as the readiness check on the local REST listener:
curl localhost:8080/health/ready
A healthy documented response is:
{"status":"ok","database":"ok","kv":"ok"}
This result proves that the HTTP server answers and that the readiness check sees the database and key-value layer as available. It does not prove that public DNS, SMTP delivery, webhooks, or outbound mail reputation are configured.
Inspect the API description
Download or inspect the OpenAPI 3.1 document from the local service:
curl http://localhost:8080/openapi.json
You can also open http://localhost:8080/docs in a browser on a machine that can reach the host. The interactive documentation includes a request client for the running server. Treat that interface as an administrative surface because it helps users discover and call API operations.
Do not create the Localtonet tunnel until the local readiness request succeeds consistently. If localhost:8080 fails on the Sentio host, changing relay servers, public URLs, or tunnel settings cannot repair the underlying Sentio deployment.
Secure Sentio before remote agent access
Sentio's migrations create a bootstrap administrator tenant and a bootstrap API key. That initial key has wildcard scope and is intended to get the installation started. It is not an appropriate credential to leave in place when the API becomes reachable from an untrusted network.
We deliberately do not reproduce the bootstrap secret in this article. Retrieve the initial value from the version-specific Sentio deployment material on the host, use it only over a trusted local connection, create a replacement through the tenant API-key endpoint, update the authorized application, verify the replacement, and then delete the bootstrap key. Never place a real key in a shell history recording, source repository, frontend application, issue report, screenshot, or Localtonet tunnel configuration.
The seeded key has wildcard scope. Exposing port 8080 before replacing and deleting that credential can place the complete administrative API at risk. Create narrowly scoped keys for agent workloads and keep administrative credentials separate from runtime credentials.
Apply least privilege to agents
An agent that only sends messages should not automatically receive tenant administration privileges. Likewise, a process that reads mailbox events may not need domain, key, suppression, or IP-pool administration. Use Sentio's scoped API-key model to give each integration only the operations it requires.
Separate credentials by environment and workload. Development, staging, production, administrative automation, and individual customer integrations should not all share one wildcard key. This limits the impact of an accidental log entry or compromised agent runtime and makes revocation more targeted.
Review all listeners
Creating an HTTP tunnel for port 8080 does not hide other host ports already published by Docker. Review the host's firewall, cloud security rules, router configuration, and Compose port mappings independently. In particular, do not expose the MinIO console merely because Sentio uses MinIO internally.
Sentio ships without TLS certificates, so it starts its own listeners in plaintext where applicable and skips port 465. For the REST workflow in this article, Localtonet provides a public HTTPS address for the HTTP tunnel, while the client connects from the device to the local target. Do not infer from that arrangement that unrelated SMTP listeners or direct host access have been secured.
Optional Sentio TLS mount
Sentio's documented Compose configuration can mount a certificate and key from a local directory. It expects files named cert.pem and key.pem:
services:
sentio:
volumes:
- ./tls:/etc/sentio/tls:ro
Certificate issuance, renewal, hostname selection, and private-key permissions depend on your deployment and are not established by the supplied evidence. Do not insert placeholder certificates or reuse unrelated private keys.
Expose the Sentio REST API with Localtonet

Once localhost:8080/health/ready succeeds and the bootstrap key has been replaced, the Sentio REST listener is ready for the remote-access layer. An HTTP tunnel is the appropriate Localtonet tunnel type because the target is an HTTP API.
Our client runs on the Sentio host, or on another device that can reach the Sentio host, and establishes an outbound connection to a Localtonet relay server. You do not need inbound router port forwarding, firewall changes, VPN setup, or a public IP address for this HTTP access path. The resulting tunnel provides a public HTTPS URL while it is started and its selected client remains connected.
The tunnel makes the target reachable. It does not remove the need for Sentio bearer authentication, scoped keys, secret handling, webhook validation, or least-privilege design. Assume that an internet-reachable URL may be discovered and protect every sensitive API operation accordingly.
Install and run the Localtonet client
Install our client on the Sentio host or on a device that can reach its HTTP listener. Keep the client running for as long as remote agents need the tunnel.
Authenticate and select the client device
Use the device-specific authentication token associated with the client that will run the tunnel. Treat the token as a secret and never copy it into documentation, source code, screenshots, or agent prompts.
Select an available relay server
Choose a server or region currently available in the Localtonet dashboard. Availability can vary, so obtain the value from the current product rather than copying a hardcoded server code from an article.
Create the HTTP tunnel configuration
Select an HTTP tunnel and set its local target to the Sentio REST listener. When the Localtonet client runs on the same host, use local IP address 127.0.0.1 and port 8080. Choose the available process type appropriate to your deployment: Random Sub Domain, Custom Sub Domain, or Custom Domain. Exact custom-domain DNS requirements must be checked in the current dashboard documentation before changing DNS.
Start the tunnel
Creating the configuration does not start it. Press the Start button and wait for the tunnel to run. Localtonet then assigns the applicable public HTTPS address.
Verify the public route and manage its lifecycle
Request the Sentio readiness path through the assigned public URL, then test an authenticated operation with a narrowly scoped non-bootstrap key. Stop the tunnel when remote access is no longer required, or delete it if the configuration should not be retained.
The public readiness check follows this shape, with the assigned URL substituted at review time:
curl https://your-assigned-host/health/ready
Do not copy a private production hostname or bearer token into shared troubleshooting output. For an authenticated API test, use a secret-management method suitable for your agent platform and verify that the selected key can perform only the required operation.
Selecting the local target correctly
Use 127.0.0.1:8080 only when the Localtonet client and the published Sentio API port are on the same machine. If our client runs on a different device, the local target must be an address that device can actually reach. Confirm that route privately before creating the tunnel.
If you run the Localtonet client inside a container, 127.0.0.1 refers to that container rather than automatically referring to the Sentio container or Docker host. Container networking varies by deployment, and the supplied evidence does not establish a universal container hostname. Test connectivity from the exact environment where our client runs instead of guessing.
What remote agents should use
Give the remote agent the public API base URL and a scoped Sentio bearer credential through the agent platform's secret store. The agent can use the OpenAPI description to understand supported operations, but unrestricted exposure of /docs or /openapi.json may disclose unnecessary API structure. Whether to permit those paths depends on your surrounding access controls and threat model.
Keep inbound webhooks conceptually separate from the REST API. The tunnel described here lets a remote process call Sentio. If Sentio must call a webhook running on another private machine, that webhook endpoint needs its own reachable HTTPS route and its own request-verification policy. Do not reuse a Sentio API key as a webhook signing secret.
Routine operation and maintenance
Inspect status and logs
Use Compose to see the current service state and inspect the two most relevant startup logs:
docker compose ps
docker compose logs sentio-migrate
docker compose logs -f sentio
A tunnel can remain connected while Sentio is unhealthy, so monitor the application and tunnel as separate layers. Sentio readiness confirms the local application state. Localtonet tunnel status confirms that the selected client is connected and the tunnel is running.
Stop Sentio without deleting its data
docker compose down
This stops the stack while retaining its Docker volumes. After Sentio stops, a Localtonet HTTP tunnel targeting port 8080 has no healthy application to forward to. Stop the tunnel as well if maintenance should not leave a public endpoint returning connection errors.
Delete the stack and its volumes
docker compose down -v
-v option deletes volumes
Use this only when you intentionally want to remove the Compose-managed data. It is not an ordinary restart command. Verify your backup and recovery plan before deleting any production volume.
Plan upgrades rather than following moving code
Review release notes, container tags, migration behavior, and compatibility before upgrading. Test the target release against a copy of the deployment and confirm the readiness endpoint, authenticated API operations, queue processing, storage access, and agent integration.
The exact backup tools and upgrade sequence depend on whether you use Compose-managed PostgreSQL and MinIO or external services. The supplied evidence does not define one universal production backup command, so we do not invent one. At minimum, a recovery plan must account for persistent database state and stored message or attachment objects, not just the Git checkout.
Troubleshooting Sentio and Localtonet
| Symptom | Likely area | What to check |
|---|---|---|
localhost:8080 refuses connections |
Sentio startup or port publication | Run docker compose ps, inspect Sentio logs, and confirm that another process is not using port 8080. |
Migration reports that uuidv7() does not exist |
PostgreSQL version | Confirm that the actual connected database is PostgreSQL 18 or newer. |
| Readiness reports a database problem | PostgreSQL availability or configuration | Inspect database and migration logs, then verify that initialization completed successfully. |
| Readiness reports a key-value problem | Redis or Valkey | Check the key-value service state and logs rather than changing the HTTP tunnel. |
| Port 25 cannot bind | Existing mail server or privileged port restrictions | Check for Postfix, Exim, or another listener. On a manual non-root Linux binary, binding port 25 requires the appropriate capability. |
| Port 465 is not listening | Missing Sentio TLS certificates | This is expected when no certificate and key are mounted. Sentio skips the implicit-TLS listener without them. |
| Local readiness works but the public URL fails | Localtonet lifecycle or target | Confirm that the selected client is connected, the tunnel was started, and the target is the reachable Sentio address and port. |
| Public readiness works but API calls return unauthorized | Sentio authentication | Check bearer-token handling, key scope, revocation status, and tenant association. Do not weaken authentication to fix it. |
| The URL works only while a terminal is open | Client lifecycle | Ensure the Localtonet client remains running on the selected device. A tunnel is available only while its client is connected and the tunnel is running. |
Resolve a port 25 conflict during evaluation
If another service already uses port 25, Sentio documents a Compose override that replaces its published port list:
services:
sentio:
ports: !override
- "2525:25"
- "4465:465"
- "5587:587"
- "8080:8080"
Save that content as docker-compose.override.yml. The file is loaded automatically and is ignored by the repository. This can resolve local conflicts for evaluation, but remapping inbound SMTP from public port 25 to 2525 does not make normal internet MX delivery use port 2525. Other mail servers ordinarily connect to the standard SMTP port.
Separate application errors from tunnel errors
Use a layered test sequence:
- Check the Compose service state.
- Call
localhost:8080/health/readyon the Sentio host. - If the Localtonet client is on another device, call the private Sentio address from that device.
- Confirm that the selected Localtonet client is connected.
- Confirm that the HTTP tunnel is started.
- Call the public readiness URL.
- Finally, test a permitted authenticated operation with a scoped key.
This sequence identifies the first broken layer. For example, a successful public readiness response combined with a failed authenticated request usually points to Sentio authorization or request formatting, not to Localtonet connectivity.
Why production SMTP is a separate project
The HTTP tunnel solves remote access to Sentio's REST API. It does not by itself make the installation a deliverable, reputable public mail server.
Receiving internet email requires control over a domain and correct MX records that lead other mail systems to an SMTP listener. Sending reliably also involves forward and reverse DNS alignment, PTR records controlled by the address owner, SPF, DKIM, DMARC, TLS, reputation, suppression handling, bounce processing, complaint feedback, and provider-specific policies. Port 25 may be blocked by a hosting provider or ISP, and a nonstandard replacement port is not a transparent substitute for server-to-server SMTP.
Sentio includes substantial protocol and authentication functionality, but those capabilities still require deployment-specific DNS and infrastructure. Localtonet supports raw TCP tunnels, yet that fact alone does not establish that a relay endpoint is appropriate for production SMTP, that it provides the required reverse DNS, or that receiving providers will accept mail from it. No such deliverability claim is made here.
A practical first deployment is to install Sentio, verify the API, rotate credentials, and let authorized agents reach port 8080 through Localtonet. Treat public SMTP delivery, DNS publication, certificates, and mail reputation as a separate production workstream.
Frequently asked questions
What hardware does the Docker installation require?
The documented quick-start baseline is Docker Engine 24 or newer with the Compose plugin, approximately 4 GB of RAM, and approximately 8 GB of disk. The published Sentio image supports Linux AMD64 and ARM64. Production capacity depends on message volume, attachment storage, retention, scanning load, and queue activity, so the quick-start baseline should not be treated as a universal sizing guarantee.
Which Sentio endpoint should I expose through Localtonet?
For remote agent API access, expose the HTTP REST listener on port 8080. When the Localtonet client runs on the same host, the target is 127.0.0.1:8080. Verify /health/ready locally before creating the tunnel.
Does creating a Localtonet tunnel automatically start it?
No. Creating a tunnel and running it are separate lifecycle actions. After configuring the HTTP target, press Start. The tunnel remains available only while the selected client is connected and the tunnel is running.
Does the public HTTPS URL replace Sentio authentication?
No. The URL provides network reachability to the local HTTP service. Sentio's bearer authentication, scoped API keys, tenant boundaries, secret storage, and authorization policies remain necessary. Rotate and delete the bootstrap key before enabling remote access.
Can I run Sentio without Docker?
Yes, but a manual deployment requires a stable Rust toolchain, CMake, libclang-dev, a C toolchain, PostgreSQL 18 or newer, Redis or Valkey, NATS with JetStream, and S3-compatible storage. You must also supply correct configuration, apply migrations, and manage the service. Docker Compose is the safer documented quick-start path because it brings up the complete dependency stack.
Why does Sentio require PostgreSQL 18?
Sentio's schema uses the built-in uuidv7() function introduced in PostgreSQL 18. Running migrations against an older PostgreSQL server causes a missing-function failure. Confirm the actual server version before installation rather than assuming that a client package version identifies the database server.
Can I expose Sentio's SMTP ports with the same HTTP tunnel?
No. An HTTP tunnel is appropriate for the REST API, not raw SMTP. Localtonet supports raw TCP tunnels, but production mail delivery also depends on MX records, port 25 reachability, PTR and other DNS records, TLS, authentication alignment, reputation, and recipient policies. Those requirements must be designed and validated separately.
What happens if I stop Sentio but leave the tunnel running?
The tunnel can remain active, but its local target will not provide a healthy response because nothing is serving the Sentio API on port 8080. During planned maintenance, stop the tunnel if you do not want the public endpoint to remain reachable while the application is unavailable.
Connect your self-hosted Sentio API with Localtonet
Install Sentio, confirm its local readiness endpoint, replace the bootstrap administrator key, and then create an HTTP tunnel to port 8080 for your authorized remote agent workflow.
Get Started Free โ