Skip to main content

Gateway Operations

The gateway is the channel runtime. It runs against one selected profile, serves remote adapters, routes approvals, and writes all state to that profile's local directory. Changing the active profile does not mutate a running gateway.

Profile boundary

Every gateway process is bound to the profile selected at start time. The profile owns:

  • Gateway config (config.json)
  • Gateway state (gateway/)
  • Gateway logs (logs/gateway.log)
  • Channel tokens (.env)
  • Session database (sessions.sqlite, global but profile-scoped by profile_id)
# Run foreground gateway for the current active profile
estacoda gateway run

# Run foreground gateway for a specific profile
estacoda gateway run --profile work

Run and start

estacoda gateway run # Foreground supervisor. Logs in terminal.
estacoda gateway run --dry-run # Readiness check only. No lock, no PID, no adapters.
estacoda gateway run --once # One supervisor pass, then exit.

estacoda gateway install # Install user-scope service.
estacoda gateway start # Start installed user-scope service.
estacoda gateway start --system # Start installed system-scope service.

gateway run is the foreground/debug path. Use it when you want logs attached to the current terminal and no service manager in the loop.

gateway run --dry-run checks adapter readiness, state directory writability, and gateway lock state without starting adapters or polling remote APIs. Use it to validate configuration before committing to a live process. gateway run --once performs one supervisor pass and exits.

gateway start starts an installed service. It defaults to the selected profile's user-scope service. gateway start --system controls only the system service. If only a system service exists and --system is omitted, the command fails closed and tells the operator to rerun with --system.

gateway install is required before gateway start. gateway start --background is deprecated and no longer creates a detached unmanaged process; use service install/start for persistent operation.

Setup activation prompt

The setup prompt that offers to install and start the gateway is titled EstaCoda Gateway.

The prompt appears:

  • During first-run onboarding when a ready channel is configured.
  • During the existing-user Setup Editor when the first ready channel is newly configured.

The prompt does not appear for non-channel setup changes, for channel edits when a ready channel already existed before that Setup Editor run, or when a managed gateway service is already installed or active.

First-run onboarding may still offer a post-apply launch prompt. Existing-user Setup Editor apply reports apply/verify state and exits the setup flow without a launch handoff. Use EstaCoda Doctor in the Setup Editor when you want read-only health checks and required fixes.

Stop

estacoda gateway stop # Graceful SIGTERM, wait up to 10s
estacoda gateway stop --force # SIGKILL if graceful fails (unmanaged mode)

If a user-scope managed service exists, stop delegates to systemd or launchd. On systemd, --force still uses systemctl stop; it does not send SIGKILL directly. If no managed service exists, stop reads the PID file, sends SIGTERM, waits, then cleans up PID/state/lock files.

Restart

estacoda gateway restart # Restart installed user-scope service
estacoda gateway restart --graceful # Alias for restart in v0.1.0
estacoda gateway restart --system # Restart system-scope service

restart delegates to the installed service using the same selection rules as start: user service by default, system service only with --system. If no managed service exists, it fails with installation guidance and does not create an unmanaged detached process. restart --graceful remains an alias for restart.

Managed services

estacoda gateway install # Install user-scope systemd/launchd service
estacoda gateway install --profile work # Install for profile "work"
estacoda gateway install --force # Replace existing service unit
sudo estacoda gateway install --system --run-as-user estacoda # System scope

estacoda gateway uninstall # Remove user-scope service
sudo estacoda gateway uninstall --system # Remove system-scope service

Supported managers: Linux systemd (user and system), macOS launchd (user).

Each profile gets its own hash-suffixed unit or plist. Multiple profiles can have independent managed services.

Operational notes:

  • Services use an explicit HOME but not your interactive shell environment.
  • Put tokens and API keys in the profile .env, not only in shell exports.
  • systemd user services may stop on logout unless linger is enabled: sudo loginctl enable-linger $USER.
  • Service output goes to the journal. Use journalctl --user -u <unit> -f for user services.
  • Source-mode installs hardcode the absolute workspace path. If the repo moves, uninstall and reinstall.
  • Generated services invoke gateway run --profile <id>.
  • gateway start, gateway stop, and gateway restart default to the installed user service. Use --system for an installed system service.

Diagnostics

estacoda gateway diagnose # Per-channel readiness check
estacoda gateway status # Full gateway status

gateway diagnose checks:

  • Telegram token presence, allowed users/chats
  • Discord token presence
  • Email IMAP/SMTP hosts, credentials, addresses
  • WhatsApp unofficial-API gate, isolated bridge package/readiness, auth directory writability, device pairing, user authorization, dmPolicy, groupPolicy, mode, and queue pressure
  • Cron directory permissions

Returns exit code 1 if any warnings exist.

gateway status surfaces:

  • Service manager state
  • Process state
  • All configured channels with ready/configured/disabled state
  • Delivery router platforms
  • Active surface pointers
  • Pending approvals count
  • Cron job summary and recent failures
  • Recent delivery errors
  • Missing config/env warnings
  • Bounded active-subagent summaries when the active runtime exposes delegated child work

Channel enable and disable

estacoda channels enable telegram
estacoda channels disable telegram

Valid names: telegram, discord, email, whatsapp (case-insensitive).

enable sets enabled: true in profile config. disable sets enabled: false. Both are idempotent and preserve other fields.

WhatsApp setup

estacoda whatsapp

WhatsApp setup uses one shared QR flow. It can be launched from first-run onboarding optional capabilities, the existing-user Setup Editor, or the standalone estacoda whatsapp command. Each surface warns about the unofficial Baileys-backed transport, keeps dependencies inside scripts/whatsapp-bridge/, checks bridge package readiness, asks before dependency repair, renders a QR code in the terminal, and writes config/session state only after QR pairing succeeds. Dependency decline/failure and QR timeout/failure leave WhatsApp config unchanged. WhatsApp pairing-code setup is not exposed.

If no allowed senders are entered, the wizard writes dmPolicy: "pairing" so the device is linked but waiting for secure user authorization. Logged-out state requires explicit re-pair/reset of only the selected profile's WhatsApp auth directory.

Channel maturity

ChannelMaturityNote
TelegramLive-provenFirst-party remote channel for v0.1.0
DiscordPresent, not live-provenAdapter exists; live validation incomplete
EmailPresent, not live-provenAdapter exists; attachments not supported
WhatsAppOperational with external API riskGated behind experimental: true. Uses unofficial Baileys API.

Approval queue

Gateway approvals use a durable pending_approvals table in the session database. Rows are profile-scoped by profile_id. Pending approvals are ask-only: deterministic deny results and hardline blocks never become approvable queue rows.

Command payloads are transient and redacted after resolution. List and history surfaces use command preview/hash, not raw payload.

estacoda gateway approvals # List pending approvals
estacoda gateway approvals approve <id>
estacoda gateway approvals deny <id>

Busy policy

When a user sends input while the agent is already processing:

PolicyBehavior
reject (default)Reply immediately with a busy message
queueBuffer and process after the current turn
interruptAbort the current turn and start a new one

If the active turn has running subagents, ordinary messages are queued under interrupt instead of aborting the parent turn. Control commands still bypass: /stop aborts the parent turn and child work, /approve and /deny resolve approvals, /status can report bounded active-subagent state, and model/control commands keep their existing bypass behavior.

Configure per-channel in profile config.json:

{
"channels": {
"telegram": {
"busyPolicy": "queue",
"queueDepth": 3
}
}
}

queueDepth is clamped to [1, 10]. Invalid values fall back to reject with a warning.

Voice reply

Gateway voice reply mode is available when voice state manager and TTS config are present. Auto-TTS is text-first and fail-open. Generated auto-TTS media is ephemeral and profile-temp scoped.

/voice on|all|off|status

/voice on sets the chat to voice_only. /voice all (or /voice tts) enables TTS replies. /voice off disables auto-TTS. /voice status reports the current mode.

Voice state is stored in:

~/.estacoda/profiles/<profile-id>/gateway/voice-mode.json

Telegram pairing

Telegram uses bot-token-based pairing. The bot token must be present in the profile .env as ESTACODA_TELEGRAM_BOT_TOKEN (or the env var named in botTokenEnv). Allowed users and chats must be configured before the adapter accepts messages.

estacoda channels status telegram

Telegram streaming

Telegram streaming is an experimental delivery option under channels.telegram.streaming.enabled. It defaults to enabled for configured Telegram channels. Set channels.telegram.streaming.enabled to false to opt out. When enabled, provider tokens edit Telegram messages during a turn, tool boundaries seal the current streamed message, tool progress appears below that sealed message, and later provider tokens start a new streamed message below the progress entry.

The stream is delivery-only. Final response.text remains authoritative, and session state, memory, tool execution, approvals, artifacts, and workflow state are unchanged. Partial edits use lightweight HTML escaping. The final edit or fallback delivery uses the normal Telegram formatter.

Operational constraints:

  • Telegram streaming runs before normal final-text routing. If streaming cannot deliver the completed answer, ChannelGateway falls back to normal DeliveryRouter delivery.
  • A turn abort signal is required.
  • Provider fallback/failure cleanup, Telegram flood-control degradation, oversized partial payloads, approval/artifact ambiguity, cancellation, or final edit failure can force normal final text fallback.
  • Active-turn degradation does not globally disable streaming for future turns.

Rollback is a config change:

{
"channels": {
"telegram": {
"streaming": {
"enabled": false
}
}
}
}

Restart or reload the gateway process bound to that profile after changing the setting.

Logs

Gateway logs for the active profile:

~/.estacoda/profiles/<profile-id>/logs/gateway.log

In foreground mode (gateway run), logs also appear in the terminal. In managed-service mode, logs go to the profile log file or the system journal.

Failure modes

SymptomLikely causeRepair
Gateway fails to startMissing token or env varAdd token to profile .env, run gateway diagnose
Channel not readyUnauthorized user or missing allowlistConfigure the channel allowlist (allowedUsers/allowedGroups; WhatsApp setup calls them allowed senders)
Wrong profile behaviorGateway started against a different profileCheck active-profile.json or use --profile
Stale runtime cacheOld session dataRestart gateway or run gateway restart
Approval expiryPending approval timed outRe-issue the command; approvals have a TTL
Hard safety blockCommand matches hardline floorRephrase the command; hard blocks cannot be overridden

Gateway update mode

estacoda update --gateway is the non-interactive update path for managed gateway deployments.

  • Logs to ~/.estacoda/logs/update.log.
  • After a successful managed-source update, attempts to restart the gateway service through the service-manager abstraction.
  • If no managed service is detected, prints a manual restart instruction: estacoda gateway restart.
  • Never restarts arbitrary user processes.

For full update internals, see Update Operations.

Gateway teardown during uninstall

estacoda uninstall tears down the gateway before removing install code or user data.

  • Uses the service-manager abstraction (estacoda gateway uninstall-service).
  • No raw pkill, killall, systemctl, or launchctl calls in the uninstall path.
  • On Termux, system service removal is skipped; known wrapper paths are cleaned best-effort.
  • Happens before code removal and before --purge data deletion.

What is not documented here

  • Full slash-command reference is not included here; see Gateway for user-facing gateway behavior.