Skip to content

sekia-ai/sekia

CI Dependabot Updates pages-build-deployment Release

sekia

A multi-agent event bus for automating workflows across GitHub, Gmail, Google Calendar, Linear, and Slack. Built on embedded NATS with JetStream.

Documentation · Website

Seven binaries — sekiad (daemon), sekiactl (CLI), four agents (sekia-github, sekia-slack, sekia-linear, sekia-google), and sekia-mcp (MCP server) — communicate over NATS. The daemon and CLI also use a Unix socket.

Architecture

┌──────────────────────────────────────────────────────────────────┐
│                              sekiad                              │
│                                                                  │
│  ┌───────────────┐  ┌──────────┐  ┌───────────┐  ┌────────────┐  │
│  │ Embedded NATS │  │ Registry │  │ Workflow  │  │ Sentinel   │  │
│  │ + JetStream   │  │          │  │  Engine   │  │ (AI checks)│  │
│  └───────┬───────┘  └──────────┘  └─────┬─────┘  └────────────┘  │
│          │                              │                        │
│  ┌───────┴───────┐  ┌──────────┐  ┌─────┴─────┐  ┌────────────┐  │
│  │ AI Client     │  │ Skills   │  │ Conver-   │  │  HTTP API  │  │
│  │ (Anthropic)   │  │          │  │ sations   │  │            │  │
│  └───────────────┘  └──────────┘  └───────────┘  └──────┬─────┘  │
│                                                         │        │
│  ┌────────────────────────────────────────────────────┐ │        │
│  │ Web Dashboard (htmx + SSE)                  :8080  │ │        │
│  └────────────────────────────────────────────────────┘ │        │
└─────────────────────────────────────────────────────────┼────────┘
               │ NATS (in-process)                        │ Unix socket
               │                                          │
    ┌──────────┼────────────────────────────┐       ┌─────┴──────┐
    │          │                            │       │  sekiactl  │
    │    ┌─────┴──────┐   ┌──────────┐      │       └────────────┘
    │    │ *.lua      │   │ SKILL.md │      │       ┌────────────┐
    │    │ workflows  │   │ handlers │      │       │ sekia-mcp  │
    │    └────────────┘   └──────────┘      │       │ (stdio)    │
    │────────────┼────────────┼─────────────│       └────────────┘
    ▼            ▼            ▼             ▼
┌────────┐  ┌─────────┐  ┌──────────┐  ┌──────────┐
│ sekia- │  │ sekia-  │  │ sekia-   │  │ sekia-   │
│ github │  │ slack   │  │ linear   │  │ google   │
└───┬────┘  └────┬────┘  └────┬─────┘  └────┬─────┘
    │            │            │             │
    ▼            ▼            ▼             ▼
  GitHub      Slack        Linear     Gmail/Calendar

Startup sequence

  1. Start embedded NATS with JetStream
  2. Create registry (subscribes to sekia.registry and sekia.heartbeat.>)
  3. Start workflow engine, load .lua files, optionally start file watcher
  4. Start HTTP API on Unix socket
  5. Block on OS signal or stop channel
  6. Shutdown in reverse order

NATS subjects

Subject Purpose
sekia.registry Agent registration announcements
sekia.heartbeat.<name> Per-agent heartbeats (30s interval)
sekia.events.<source> Event publishing
sekia.commands.<name> Command delivery to agents

Install

Homebrew (macOS/Linux)

Each component is a separate formula. Install what you need:

# Daemon + CLI (required)
brew install sekia-ai/tap/sekia

# Agents (install the ones you need)
brew install sekia-ai/tap/sekia-github
brew install sekia-ai/tap/sekia-slack
brew install sekia-ai/tap/sekia-linear
brew install sekia-ai/tap/sekia-google
brew install sekia-ai/tap/sekia-mcp

Each formula (except sekia-mcp) includes a launchd service. Use brew services to manage them:

# Start the daemon as a background service
brew services start sekia

# Start agents as background services
brew services start sekia-github
brew services start sekia-slack

# Check running services
brew services list

# Stop a service
brew services stop sekia-github

Logs are written to $(brew --prefix)/var/log/<formula>.log.

From source

go install github.com/sekia-ai/sekia/cmd/sekiad@latest
go install github.com/sekia-ai/sekia/cmd/sekiactl@latest
go install github.com/sekia-ai/sekia/cmd/sekia-github@latest
go install github.com/sekia-ai/sekia/cmd/sekia-slack@latest
go install github.com/sekia-ai/sekia/cmd/sekia-linear@latest
go install github.com/sekia-ai/sekia/cmd/sekia-google@latest
go install github.com/sekia-ai/sekia/cmd/sekia-mcp@latest

Docker

# Run the full stack
docker compose up

# Or just the daemon
docker compose up sekiad

Agent credentials are read from environment variables. Copy .env.example to .env and fill in your tokens:

cp .env.example .env
# Edit .env with your GITHUB_TOKEN, SLACK_BOT_TOKEN, etc.
docker compose up

Individual images can be built with targets:

docker build --target sekiad -t sekia/sekiad .
docker build --target sekia-github -t sekia/sekia-github .

Getting started

Build

go build ./cmd/sekiad ./cmd/sekiactl ./cmd/sekia-github ./cmd/sekia-slack ./cmd/sekia-linear ./cmd/sekia-google ./cmd/sekia-mcp

Run the daemon

./sekiad

Query status

./sekiactl status
./sekiactl agents
./sekiactl workflows

Configuration

sekia uses TOML config files searched in /etc/sekia, ~/.config/sekia, and .. Environment variables with the SEKIA_ prefix are also supported.

Defaults:

Key Default
server.socket ~/.config/sekia/sekiad.sock (or $XDG_RUNTIME_DIR/sekia/sekiad.sock)
server.listen 127.0.0.1:7600
nats.embedded true
nats.data_dir ~/.local/share/sekia/nats
workflows.dir ~/.config/sekia/workflows
workflows.hot_reload true
workflows.verify_integrity false
ai.provider anthropic
ai.model claude-sonnet-4-20250514
ai.max_tokens 1024
ai.persona_path ~/.config/sekia/persona.md
skills.dir ~/.config/sekia/skills
conversation.max_history 50
conversation.ttl 1h
sentinel.enabled false
sentinel.interval 5m
web.listen (empty — disabled)

See configs/sekia.toml for an example.

Secrets Encryption

Secret values in config files can be encrypted or referenced inline using three backends:

Format Backend Description
ENC[...] age Local age keypair encryption
KMS[...] AWS KMS Encrypt/decrypt via AWS KMS API
ASM[...] AWS Secrets Manager Fetch plaintext secret by name/ARN
# age: generate keypair + encrypt
sekiactl secrets keygen
sekiactl secrets encrypt "ghp_mytoken123"    # → ENC[...]

# AWS KMS: encrypt with a KMS key
sekiactl secrets kms-encrypt --key-id alias/sekia "ghp_mytoken123"  # → KMS[...]

All three formats can be mixed in the same config file:

[github]
token = "ENC[YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IF...]"

[webhook]
secret = "KMS[AQIDAHhB...]"

[nats]
token = "ASM[prod/sekia/nats-token]"

For age, the decryption key can live off-machine — set SEKIA_AGE_KEY (raw key) or SEKIA_AGE_KEY_FILE (path). For AWS backends, credentials use the standard SDK default chain (env, profile, instance role). See SECURITY.md for details.

Web Dashboard

sekia includes an embedded web dashboard for monitoring agents, workflows, and live events. Enable it by setting web.listen:

[web]
listen = ":8080"

Then open http://localhost:8080/web in your browser. The dashboard shows:

  • System status — uptime, NATS status, agent/workflow counts
  • Connected agents — name, status, version, event/error counters, last heartbeat
  • Loaded workflows — name, handler count, event/error counters, patterns
  • Live events — real-time event stream via Server-Sent Events (SSE)

Status and agent/workflow panels auto-refresh every 5–10 seconds via htmx. The event stream updates in real-time. No external dependencies — htmx and Alpine.js are vendored and embedded in the binary.

Security hardening is built in: Content-Security-Policy, X-Frame-Options, X-Content-Type-Options, and Strict-Transport-Security headers are set on every response. SSE connections are capped at 50 to prevent DoS. CSRF protection via double-submit cookie is enforced on all state-changing requests.

API

The daemon exposes an HTTP API over its Unix socket.

Endpoint Description
GET /api/v1/status Daemon status, uptime, agent count, workflow count
GET /api/v1/agents List registered agents with capabilities and stats
GET /api/v1/workflows List loaded workflows with handler patterns and stats
POST /api/v1/workflows/reload Reload all workflows from disk
GET /api/v1/skills List loaded skills with descriptions and triggers

Agent SDK

The pkg/agent package provides an SDK for building agents that auto-register and send heartbeats.

package main

import (
	"github.com/sekia-ai/sekia/pkg/agent"
	"github.com/sekia-ai/sekia/pkg/protocol"
)

func main() {
	a, err := agent.New(agent.Config{
		Registration: protocol.Registration{
			Name:         "my-agent",
			Version:      "0.2.3",
			Capabilities: []string{"read", "write"},
			Commands:     []string{"sync"},
		},
		NATSURLs: "nats://127.0.0.1:4222",
	})
	if err != nil {
		panic(err)
	}
	defer a.Close()

	// Use a.Conn() for custom NATS subscriptions
	// Call a.RecordEvent() / a.RecordError() to update counters
}

Workflows

Workflows are Lua scripts that react to events and send commands to agents. Place .lua files in the workflow directory (default ~/.config/sekia/workflows/).

-- ~/.config/sekia/workflows/github_labeler.lua

sekia.on("sekia.events.github", function(event)
    if event.type ~= "github.issue.opened" then return end

    local title = string.lower(event.payload.title or "")
    local label = "triage"
    if string.find(title, "bug") then label = "bug" end

    sekia.command("github-agent", "add_label", {
        owner  = event.payload.owner,
        repo   = event.payload.repo,
        number = event.payload.number,
        label  = label,
    })

    sekia.log("info", "labeled issue #" .. event.payload.number)
end)

Lua API

Function Description
sekia.on(pattern, handler) Register handler for NATS subject pattern (* and > wildcards)
sekia.publish(subject, type, payload) Emit a new event
sekia.command(agent, command, payload) Send command to an agent
sekia.log(level, message) Log a message (debug, info, warn, error)
sekia.ai(prompt [, opts]) Call an LLM and return the response text. Options: model, max_tokens, temperature, system
sekia.ai_json(prompt [, opts]) Like sekia.ai but requests JSON and returns a parsed Lua table
sekia.skill(name) Returns full instructions for a named skill (from SKILL.md files)
sekia.conversation(platform, channel, thread) Returns a conversation handle with :append(), :reply(), :history(), :metadata()
sekia.schedule(interval_seconds, handler) Register a timer-driven handler (minimum 1s interval)
sekia.name The workflow's name (derived from filename)

Workflows run in a sandboxed Lua VM with only base, table, string, and math libraries available. Dangerous functions (os, io, debug, dofile, load) are removed.

When hot_reload is enabled (default), editing or adding .lua files automatically reloads the affected workflows.

Workflow Integrity Verification

When workflows.verify_integrity is enabled, the daemon verifies each .lua file against a SHA256 manifest (workflows.sha256) before loading it. This prevents tampered or unsigned workflows from executing.

[workflows]
verify_integrity = true

Generate the manifest with sekiactl:

sekiactl workflows sign
# Signed 3 workflow(s) in ~/.config/sekia/workflows
# a1b2c3...  github-labeler.lua
# d4e5f6...  slack-responder.lua
# 789abc...  linear-triage.lua

The manifest uses sha256sum-compatible format. When hot-reload is enabled, updating the manifest file automatically triggers a full reload of all workflows.

AI-Powered Workflows

Workflows can call an LLM using sekia.ai() and sekia.ai_json(). Configure the AI provider in sekia.toml:

[ai]
# api_key can also be set via SEKIA_AI_API_KEY env var
api_key = ""
model = "claude-sonnet-4-20250514"
max_tokens = 1024

Both functions are synchronous and return (result, nil) on success or (nil, error_string) on failure. If no API key is configured, they return nil, "AI not configured".

sekia.ai(prompt, opts) returns the raw response text. sekia.ai_json(prompt, opts) requests a JSON response and parses it into a Lua table.

Options (all optional): model, max_tokens, temperature, system.

Example — AI issue classifier (configs/workflows/ai-issue-classifier.lua):

sekia.on("sekia.events.github", function(event)
    if event.type ~= "github.issue.opened" then return end

    local prompt = "Classify this GitHub issue. Reply with exactly one word: bug, feature, question, or docs.\n\n"
        .. "Title: " .. (event.payload.title or "") .. "\n"
        .. "Body: " .. (event.payload.body or "")

    local result, err = sekia.ai(prompt, {
        max_tokens = 16,
        temperature = 0,
    })

    if err then
        sekia.log("error", "AI classification failed: " .. err)
        return
    end

    local label = string.lower(string.gsub(result, "%s+", ""))
    sekia.command("github-agent", "add_label", {
        owner  = event.payload.owner,
        repo   = event.payload.repo,
        number = event.payload.number,
        label  = label,
    })
end)

Example — AI PR summary (configs/workflows/ai-pr-summary.lua):

sekia.on("sekia.events.github", function(event)
    if event.type ~= "github.pr.opened" then return end

    local result, err = sekia.ai(
        "Write a brief one-paragraph summary of this pull request.\n\n"
            .. "Title: " .. (event.payload.title or "") .. "\n"
            .. "Body: " .. (event.payload.body or ""),
        { system = "You are a helpful code review assistant. Be concise and technical." }
    )

    if err then return end

    sekia.command("github-agent", "create_comment", {
        owner  = event.payload.owner,
        repo   = event.payload.repo,
        number = event.payload.number,
        body   = "**AI Summary:** " .. result,
    })
end)

Persona — Agent Identity

A markdown file defines your agent's personality, communication style, values, and boundaries. This content is automatically prepended to every AI system prompt.

# Create your persona
cat > ~/.config/sekia/persona.md << 'EOF'
You are a helpful engineering assistant. Be concise, technical, and direct.
When in doubt, ask for clarification rather than guessing.
EOF

Config: ai.persona_path in sekia.toml (default ~/.config/sekia/persona.md).

Skills — Capability Definitions

Skills are directory-based capability definitions with YAML frontmatter and natural language instructions. Place them in the skills directory (default ~/.config/sekia/skills/).

<!-- ~/.config/sekia/skills/pr-review/SKILL.md -->
---
name: pr-review
description: Reviews GitHub PRs for code quality
triggers:
  - github.pr.opened
version: "1.0"
---
When reviewing a PR:
1. Check for test coverage
2. Look for security issues
3. Approve if only minor style issues

Skills metadata is automatically injected into AI prompts. Use sekia.skill(name) in workflows to get full instructions for a specific skill. Optional handler.lua files in skill directories are auto-loaded as workflows.

Conversations — Multi-Turn State

The conversation API provides in-memory multi-turn conversation state with TTL eviction:

sekia.on("sekia.events.slack", function(event)
    if event.type ~= "slack.mention" then return end

    local conv = sekia.conversation("slack", event.payload.channel,
        event.payload.thread_ts or event.payload.timestamp)

    local text = event.payload.text:gsub("<@[^>]+>%s*", "")
    local response, err = conv:reply(text)
    if err then return end

    sekia.command("slack-agent", "send_reply", {
        channel = event.payload.channel,
        thread_ts = event.payload.thread_ts or event.payload.timestamp,
        text = response,
    })
end)

Conversations are keyed by (platform, channel, thread). The :reply(prompt) method appends the user message, sends the full conversation history to the LLM, and stores the assistant response. Use :metadata(key, [value]) for per-conversation state.

Scheduled Workflows — Autonomous Agents

Use sekia.schedule() to run handlers on a timer, enabling autonomous agent behavior:

sekia.schedule(300, function()  -- every 5 minutes
    local result, err = sekia.ai_json("Review open PRs needing attention", {
        system = sekia.skill("pr-review")
    })
    if err then return end

    for _, pr in ipairs(result.prs or {}) do
        sekia.command("github-agent", "create_comment", {
            owner = pr.owner, repo = pr.repo,
            number = pr.number, body = pr.review_comment,
        })
    end
end)

Sentinel — Proactive AI Checks

Sentinel reads a markdown checklist on a configurable interval, gathers system context, and asks the AI if anything needs attention:

<!-- ~/.config/sekia/sentinel.md -->
- Are there GitHub PRs open >3 days without review?
- Are there urgent Linear tickets with no assignee?
- Have any agents gone offline since last check?
[sentinel]
enabled = true
interval = "5m"

Events are published to sekia.events.sentinel and handled by workflows like any other event.

Agents

Each agent is a standalone binary that connects to the daemon's NATS bus, publishes events from an external service, and executes commands dispatched by Lua workflows.

Named Instances (Multi-Tenancy)

All agents support a --name flag for running multiple instances of the same agent type with different configurations:

# Two GitHub agents for different accounts
sekia-github --name github-personal   # reads ~/.config/sekia/sekia-github-personal.toml
sekia-github --name github-work       # reads ~/.config/sekia/sekia-github-work.toml

When --name is set:

  • Config file becomes sekia-{agent}-{name}.toml (unless --config overrides)
  • NATS registration uses the instance name (e.g., github-work instead of github-agent)
  • Command subject becomes sekia.commands.{name} — target instances from workflows via sekia.command("github-work", "add_label", payload)

Without --name, behavior is unchanged.

Running Named Instances as Background Services

Use sekiactl service to manage named instances as background services (launchd on macOS, systemd on Linux):

# Create a service
sekiactl service create sekia-github --name github-work

# Start/stop/restart
sekiactl service start github-work
sekiactl service stop github-work
sekiactl service restart github-work

# List all managed services
sekiactl service list
# NAME             BINARY         STATUS   PID
# github-work      sekia-github   running  12345
# github-personal  sekia-github   stopped  -

# Remove a service (stops and deletes)
sekiactl service remove github-work

Optional flags on create:

  • --config /path/to/config.toml — pass an explicit config file
  • --env KEY=VALUE — set environment variables (repeatable; stored in plaintext, prefer ENC[...] config values)

Logs are written to ~/.config/sekia/logs/{name}.log. The default brew services-managed instance is not affected.

GitHub Agent

Ingests GitHub events via webhooks and/or REST API polling, and executes GitHub API commands.

Webhook mode (default):

export GITHUB_TOKEN=ghp_...
./sekia-github

Point your GitHub repository's webhook settings to http://<host>:8080/webhook. Optionally set GITHUB_WEBHOOK_SECRET to verify signatures.

Polling mode — useful when the agent cannot receive inbound webhooks (e.g., behind a firewall or in local development). Both modes can run simultaneously.

# sekia-github.toml
[poll]
enabled = true
interval = "30s"
repos = ["myorg/myrepo"]

To use polling only (no webhook server), set webhook.listen = "".

Config: configs/sekia-github.toml. Env vars: GITHUB_TOKEN, GITHUB_WEBHOOK_SECRET, SEKIA_NATS_URL.

Events:

GitHub Event sekia Event Type Source
Issue opened/closed/reopened/labeled/assigned github.issue.<action> Webhook
Issue opened/closed github.issue.opened, github.issue.closed Polling
Issue updated (any change) github.issue.updated Polling only
PR opened/closed/merged/review_requested github.pr.<action> Webhook
PR opened/closed/merged github.pr.opened, github.pr.closed, github.pr.merged Polling
PR updated (any change) github.pr.updated Polling only
PR matched (cycling) github.pr.matched PR-match mode (match_prs = true)
Push github.push Webhook only
Issue comment created github.comment.created Both

Polled events include payload.polled = true so workflows can distinguish them from webhook events if needed.

PR-match mode: Set match_prs = true in [poll] to cycle through all PRs matching state (and optionally labels) every interval. Emits github.pr.matched events with full PR metadata including author, draft, labels, branches. Useful for auto-approval or triage workflows.

Commands:

Command Required Payload Action
add_label owner, repo, number, label Add a label to an issue/PR
remove_label owner, repo, number, label Remove a label
create_comment owner, repo, number, body Post a comment
close_issue owner, repo, number Close an issue
reopen_issue owner, repo, number Reopen an issue
approve_pr owner, repo, number Submit an approving review (optional: body)
add_to_project owner, repo, number, project_id Add to a Projects v2 board (optional: fields)

Example workflow: configs/workflows/github-auto-label.lua


Slack Agent

Connects to Slack via Socket Mode (WebSocket). No public URL required.

export SLACK_BOT_TOKEN=xoxb-...
export SLACK_APP_TOKEN=xapp-...
./sekia-slack

Setup: Create a Slack app at api.slack.com/apps with Socket Mode enabled. Required bot token scopes: chat:write, reactions:write, channels:history, groups:history, im:history. Generate an app-level token with connections:write scope. Enable Interactivity in your app settings to receive button click events (no Request URL needed with Socket Mode).

Config: configs/sekia-slack.toml. Env vars: SLACK_BOT_TOKEN, SLACK_APP_TOKEN, SEKIA_NATS_URL.

Events:

Slack Event sekia Event Type Payload Fields
New message (not from bot) slack.message.received channel, user, text, timestamp, thread_ts (if threaded)
Reaction added slack.reaction.added user, reaction, channel, timestamp
Channel created slack.channel.created channel_id, channel_name, creator
Message mentioning the bot slack.mention channel, user, text, timestamp
Button clicked slack.action.button_clicked action_id, value, block_id, user, user_name, channel, message_ts, message_text, trigger_id

Commands:

Command Required Payload Action
send_message channel, text, blocks (optional) Post a message. When blocks is provided (array of Block Kit objects), sends a rich message with text as notification fallback
add_reaction channel, timestamp, emoji Add a reaction to a message
send_reply channel, thread_ts, text Reply in a thread
update_message channel, timestamp, text, blocks (optional) Update an existing message. When blocks is provided, updates with rich Block Kit content

Example workflow: configs/workflows/slack-auto-reply.lua

sekia.on("sekia.events.slack", function(event)
    if event.type ~= "slack.mention" then return end

    sekia.command("slack-agent", "send_reply", {
        channel   = event.payload.channel,
        thread_ts = event.payload.timestamp,
        text      = "Hi <@" .. event.payload.user .. ">, thanks for reaching out!",
    })
end)

Linear Agent

Polls the Linear GraphQL API for updated issues and comments. No webhooks or public URL required.

export LINEAR_API_KEY=lin_api_...
./sekia-linear

Setup: Create a personal API key at linear.app/settings/api.

Config: configs/sekia-linear.toml. Env vars: LINEAR_API_KEY, SEKIA_NATS_URL.

Setting Default Description
poll.interval 30s How often to poll Linear
poll.team_filter (empty) Limit to a team key (e.g., ENG)

Events:

Trigger sekia Event Type Payload Fields
New issue (created since last poll) linear.issue.created id, identifier, title, state, priority, team, url, assignee, labels
Issue updated linear.issue.updated (same as above)
Issue moved to Done/Completed/Canceled linear.issue.completed (same as above)
New comment linear.comment.created id, body, author, issue_id, issue_identifier

Commands:

Command Required Payload Action
create_issue team_id, title, description (optional) Create a new issue
update_issue issue_id, plus state_id/assignee_id/priority Update an issue
create_comment issue_id, body Add a comment to an issue
add_label issue_id, label_id Add a label to an issue

Example workflow: configs/workflows/linear-auto-triage.lua

sekia.on("sekia.events.linear", function(event)
    if event.type ~= "linear.issue.created" then return end

    sekia.command("linear-agent", "create_comment", {
        issue_id = event.payload.id,
        body     = "Auto-triaged. Team: " .. (event.payload.team or "unknown"),
    })
end)

Google Agent

Bridges Gmail and Google Calendar to the NATS event bus via Google REST APIs with OAuth2 authentication.

# First time: authorize via browser
sekia-google auth --config configs/sekia-google.toml

# Run the agent
./sekia-google

Setup: Create OAuth credentials at console.cloud.google.com/apis/credentials with application type "Desktop app". Run sekia-google auth to authorize via browser.

Config: configs/sekia-google.toml. Env vars: GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, GOOGLE_TOKEN_PATH, SEKIA_NATS_URL.

Gmail Events:

Trigger sekia Event Type Payload Fields
New message gmail.message.received id, thread_id, message_id, from, to, subject, body, date, labels

Calendar Events:

Trigger sekia Event Type Payload Fields
New event google.calendar.event.created id, summary, description, location, start, end, organizer, attendees, html_link
Event updated google.calendar.event.updated (same as above)
Event deleted google.calendar.event.deleted id, summary, status
Event starting soon google.calendar.event.upcoming (same as created) + minutes_until

Gmail Commands:

Command Required Payload Action
send_email to, subject, body Send a new email
reply_email thread_id, in_reply_to, to, subject, body Reply to an email
add_label message_id, label Add a label to a message
remove_label message_id, label Remove a label from a message
archive message_id Archive a message (remove INBOX label)
trash message_id Move a message to trash
untrash message_id Move a trashed message back to inbox
delete message_id Permanently delete a message

Calendar Commands:

Command Required Payload Action
create_event summary, start, end Create a calendar event
update_event event_id, plus optional fields Update a calendar event
delete_event event_id Delete a calendar event

Example workflow: configs/workflows/google-calendar-notify.lua


MCP Server

Exposes sekia capabilities to AI assistants (Claude Desktop, Claude Code, Cursor) via the Model Context Protocol. Uses stdio transport — the MCP client launches sekia-mcp as a subprocess.

./sekia-mcp

Config: configs/sekia-mcp.toml. Env vars: SEKIA_NATS_URL, SEKIA_DAEMON_SOCKET.

MCP Tools:

Tool Description
get_status Daemon health, uptime, NATS status, agent/workflow counts
list_agents Connected agents with capabilities, commands, and heartbeat data
list_workflows Loaded Lua workflows with handler patterns and event/error counts
reload_workflows Hot-reload all .lua workflow files from disk
publish_event Emit a synthetic event onto the NATS bus to trigger workflows
send_command Send a command to a connected agent (Slack message, GitHub comment, etc.)

Claude Desktop setup: Add to your MCP settings (~/Library/Application Support/Claude/claude_desktop_config.json):

{
  "mcpServers": {
    "sekia": {
      "command": "sekia-mcp",
      "env": {
        "SEKIA_NATS_URL": "nats://127.0.0.1:4222",
        "SEKIA_DAEMON_SOCKET": "~/.config/sekia/sekiad.sock"
      }
    }
  }
}

Cross-Agent Workflows

The real power of sekia is connecting agents together. Here's a workflow that posts to Slack when a GitHub issue is opened, and creates a Linear tracking issue:

-- ~/.config/sekia/workflows/issue-tracker.lua

sekia.on("sekia.events.github", function(event)
    if event.type ~= "github.issue.opened" then return end

    local title = event.payload.title
    local url   = event.payload.url
    local repo  = event.payload.repo

    -- Notify the team in Slack
    sekia.command("slack-agent", "send_message", {
        channel = "C_ENGINEERING",
        text    = "New issue in " .. repo .. ": " .. title .. "\n" .. url,
    })

    -- Create a tracking issue in Linear
    sekia.command("linear-agent", "create_issue", {
        team_id     = "TEAM_ID_HERE",
        title       = "[" .. repo .. "] " .. title,
        description = "GitHub issue: " .. url,
    })
end)

Testing

go test ./...

Each agent has end-to-end integration tests that start the full daemon with embedded NATS, connect the agent in-process, and verify the complete event-to-command flow through Lua workflows.

License

Apache 2.0