Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Rustiz (Rust, agent-first)

A headless social media scheduler for AI agents: one binary, one SQLite file and no GUI. It is a Rust port of Postiz v1.47.0 (commit e424fb5), licensed AGPL-3.0 like the original. The original copyright notice is preserved in LICENSE. If you run a modified version as a network service, you must offer its source to its users.

Status: no provider has been tested against a real platform. All tests use mock servers, so expect fixes when you connect real accounts. See Porting status.

Measured

Release build on an Apple Silicon Mac, with an empty database. Throughput is 2,976 integrationList calls over MCP at 32 concurrent clients with rate limits turned off, and the Python test client may be the limit.

BinaryReadyRAM idleRAM after loadThroughput
16.9 MB117 ms19.0 MB24.4 MBabout 18,900 requests/s

Run

Self-hosted, on your own machine:

rustiz init                     # creates ~/.rustiz: secret, database and API key
rustiz providers --status       # which platforms are ready, and what each one needs

rustiz init prints the MCP config to paste into your agent. After that, rustiz mcp needs no arguments, and it receives the platform’s login redirect itself. Running init again never replaces the secret or creates a second organization.

On a server, set your public URL and put TLS in front, because the server speaks plain HTTP:

FRONTEND_URL=https://rustiz.example rustiz serve

Environment variables override ~/.rustiz/config.env; --config or RUSTIZ_CONFIG picks another file. Back up JWT_SECRET with the database: stored channel credentials can’t be read without it.

Each OAuth platform needs its own developer app, with credentials such as X_API_KEY or REDDIT_CLIENT_ID and a redirect URI of FRONTEND_URL/integrations/social/<provider>. They are the same variables Postiz uses. Some platforms may refuse a plain http://localhost redirect, in which case that platform needs a public HTTPS FRONTEND_URL.

Email is optional: without EMAIL_PROVIDER, nothing is sent. Set UPLOAD_DIRECTORY for local media, or STORAGE_PROVIDER=cloudflare|s3 for object storage. Video thumbnails need an ffmpeg binary, which the Docker image does not include.

CommandPurpose
rustiz initFirst-time setup
rustiz serveHTTP API, MCP and the scheduler (--no-worker for HTTP only)
rustiz workerThe scheduler alone
rustiz mcpMCP over stdio for a local agent, with the scheduler and login callback in-process
rustiz posts|channels|media|analytics|notifications|find-slotCLI for agents
rustiz org create|list|rotate-keyOrganizations and API keys; rotating revokes OAuth tokens
rustiz providers [--status]List platforms and their readiness

CLI

Every command prints one JSON document on stdout. Run rustiz --help for the full list.

rustiz channels connect reddit --open
rustiz posts create --channel <id> --content "Hello" --date 2030-01-01T09:00:00Z --type schedule
rustiz posts update <id> --content "Fixed typo"
rustiz posts reschedule <id> --date 2030-01-02T09:00:00Z
rustiz posts delete <id>

Without RUSTIZ_URL the CLI works on the local database and needs no server, but posts only publish while rustiz serve, worker or mcp is running. With RUSTIZ_URL and RUSTIZ_API_KEY it talks to a remote server. Exit codes: 1 runtime, 2 bad request, 3 auth, 4 not found, 5 post is publishing.

Connect an agent

Remote: https://your-host/mcp, with either Authorization: Bearer <api key> or OAuth. Clients that support OAuth discover it from the 401 response. The approval page asks for the API key, and it is the only form the server renders.

Local: paste the config rustiz init prints.

Set MCP_ALLOWED_HOSTS if clients reach the server on a host other than FRONTEND_URL.

Editing posts

PUT /public/v1/posts/{id}, PUT /public/v1/posts/{id}/date, updatePostTool and the CLI share one code path. A post keeps its id. While a job is publishing the post, changes are refused with a 409, so nothing is posted twice. Published posts can’t be edited, because platforms keep the original; republish with a date publishes one again. An ERROR post may already be live, so check the platform before retrying with publish. Deleting a post here does not remove it from a platform.

Rate limits

RouteLimit per hourSetting
POST on /public/v1/posts90 per organizationAPI_LIMIT
/mcp and other /public/v1 routes10,000 per organizationREQUEST_LIMIT
POST /oauth/authorize, /oauth/register30 per client addressfixed
POST /oauth/token120 per client addressfixed

0 turns a setting off. Counts are per process. X-Forwarded-For is ignored unless TRUSTED_PROXY_HOPS says how many proxies append to it; set that only if the server can’t be reached except through those proxies.

MCP behaviour

The server uses the official rmcp SDK and implements spec 2026-07-28. Older clients that use initialize work on the same endpoint.

  • Typical flow: providersStatusTool, connectChannelTool, integrationSchema, validatePostTool, schedulePostTool, postStatusTool.
  • Safe resends: an identical schedulePostTool call within 10 minutes, or one with the same idempotencyKey within 24 hours, returns the first result.
  • Tasks: type: "now" returns a durable task handle to clients that declare the Tasks extension. Others get a normal result and poll postStatusTool.
  • Connecting accounts: OAuth returns a login link for a human. Credential providers take customFields. Telegram, Moltbook and Farcaster return what the human must do, then the agent polls with connectionId. Channels marked inBetweenSteps are finished with triggerTool and channelSetupTool.
  • Live updates: subscriptions/listen on rustiz://posts/<id> or rustiz://groups/<id> sends a hint when a post’s state changes. It polls the database every 2 seconds (MCP_SUBSCRIPTIONS_POLL_MS), so it also sees a separate rustiz worker. Quick changes between polls collapse into the last one, and deletions are not reported. At most 4 streams per organization (MCP_SUBSCRIPTIONS_MAX_STREAMS).

Not in this project

Generating media is out of scope: agents make it with other tools and upload it. The cloud parts of a hosted product (signup, teams, billing, password emails, a dashboard) belong in a separate layer on top of the REST API.