Early this year I spent a month or so building an ATProto PDS from scratch. After the Atmosphere conference I put this aside to focus on IO, but recently I switched IO's data storage from SQLite to the Bolt key-value store, and that went so well that I decided to also use Bolt in Smile (my PDS). That led to some other rethinking of PDS data storage: I had originally copied the directory structure and SQLite schemas of the reference PDS, but with a basic PDS running I could start making changes, starting with moving to Bolt.
Repo Storage Changes
Now apart from blobs, all data associated with a repo is in a single Bolt database file. This includes the repo signing and rotation keys, which were not stored in the reference PDS SQLite. Instead, the reference PDS keeps the signing key in a file alongside the repo database and a single rotation key is common to all the repos managed by a reference PDS instance. In smile, every repo has its own rotation key, and this and the signing key are stored in the repo database file. A hash of the account's login password is also stored in the repo database. This makes it easy to move repos around!
The repo contains blob records, but the actual file storage for blobs is in a directory associated with the repo DID. The reference PDS keeps a separate directory of "temporary" blobs that it only moves into its main storage when a record is written that refers to them. Since I'm expecting much more control over my PDS users (mainly me), I dropped this and all blobs are written directly to main blob storage. I wrote a simple garbage collector to remove unused blobs whenever that seems necessary.
Running the PDS
Here's how I run my PDS as a Podman Quadlet.
The Data
Create a run directory in /opt/smile.
Put a Smile HCL configuration file there that contains:
- Your ssh user key
- The host name of your server
- Optionally, the ssh host key for your server
Here's mine, with some details redacted:
user "tim@agent.io" {
name = "Tim Burks"
public_key = "ssh-ed25519 ..."
}
host_name = "repo.works"
This will allow you to connect to your TUI and send CLI commands over SSH.
Install this configuration by running the container once. You can use Podman for that:
podman run --replace --name smile -v /opt/smile:/smile -p5500:3300 -p8080:8080 -ti ghcr.io/agentio/smile:v0.1.5 -c /smile/repoworks.hcl
The Service
I run the PDS in user mode with my user id, which corresponds to "1000" on my system.
This configuration file is in /etc/containers/systemd/users/1000/smile.container:
[Unit]
Description=Smile
After=network-online.target
[Container]
Image=ghcr.io/agentio/smile:latest
ContainerName=smile
Pull=newer
PublishPort=9090:8080
PublishPort=5500:3300
Volume=/opt/smile:/smile:rw,z
[Service]
Restart=always
[Install]
WantedBy=default.target
With this, I load the service with the following:
systemctl --user daemon-reload
systemctl --user start smile