Maia Setup Guide

From zero to a working AI executive assistant. Follow each step in order. Total time: ~15 minutes.

Choose Your Path

VM (Recommended)

Dedicated server that runs 24/7. Your agent is always on — scheduled jobs, workers, and Slack bot run even when your laptop is closed.

Cost: ~$9/mo (Hetzner) + AI engine subscription

Local Machine

Run on your own computer. Free hosting, but cron jobs and workers stop when your machine sleeps.

Cost: AI engine subscription only

Part 1: Set Up Your VM

Skip to Part 2 if using your local machine.

1

Create a Hetzner Cloud Account

Go to hetzner.com/cloud and create an account. Add a payment method.

Hetzner offers the best price/performance ratio for this use case. A CPX21 (3 vCPU, 4GB RAM) at ~$9/month is more than enough.

2

Add Your SSH Key

In the Hetzner console, go to Security > SSH Keys and add your public key.

If you don't have an SSH key yet:

Generate SSH Key
ssh-keygen -t ed25519 -C "your-email@example.com"
cat ~/.ssh/id_ed25519.pub
3

Create a Server

In the Hetzner console:

  • Location: Choose closest to you
  • Image: Ubuntu 24.04
  • Type: CPX21 (Shared vCPU, AMD) — $9/mo
  • SSH Key: Select yours
  • Name: Whatever you want (e.g., "my-agent")
4

Secure the VM

SSH in as root and run the security hardening script:

Run as root
ssh root@YOUR_SERVER_IP

# Download and run hardening script
curl -sL https://raw.githubusercontent.com/madewell-ai/agent-setup/main/vm-setup/harden.sh -o harden.sh
chmod +x harden.sh
./harden.sh maia

This will:

  • Enable automatic security updates
  • Harden SSH (key-only, no root login, fail2ban)
  • Configure UFW firewall
  • Create your non-root user
  • Install essential tools
Important: Test SSH as your new user BEFORE closing the root session:
ssh maia@YOUR_SERVER_IP

Part 2: Install the Stack

5

Install Node.js, Python, and Your AI Engine

Log in as your non-root user and run the install script:

Install software stack
curl -sL https://raw.githubusercontent.com/madewell-ai/agent-setup/main/vm-setup/install-stack.sh -o install-stack.sh
chmod +x install-stack.sh
./install-stack.sh

The script will ask which AI engine to install. See the pricing page if you're not sure which to choose.

6

Authenticate Your AI Engine

Authenticate
# For Claude Code:
claude auth
# Opens browser — follow the auth flow

# For Codex CLI:
codex auth
# Enter your API key
7

Clone and Configure

Clone repo and edit config
git clone https://github.com/madewell-ai/agent-setup.git ~/agent-setup
cd ~/agent-setup
nano config.json

Edit config.json with your details:

config.json
{
  "assistant": {
    "name": "Jarvis",        ← Your agent's name
    "role": "AI Executive Assistant"
  },
  "user": {
    "name": "Your Name",     ← Your name
    "timezone": "America/New_York",
    "location": "New York, NY"
  },
  "engine": "claude-code"    ← or "codex"
}

Part 3: Let the Agent Set Itself Up

8

Run the Self-Setup Prompt

This is the key step. You paste a prompt into your AI engine, and it configures the entire system — hooks, memory, cron, everything.

Self-setup
# Claude Code:
claude -p "$(cat ~/agent-setup/setup-prompts/claude-code-setup.md)"

# Codex CLI:
codex "$(cat ~/agent-setup/setup-prompts/codex-setup.md)"

The agent will read the instructions, set up all the hooks and scripts, create the memory system, install cron jobs, and report back what it did.

You can also copy the setup prompt from the Setup Prompt page.

Part 4: Connect Slack

9

Create a Slack App

Go to api.slack.com/apps and click Create New App > From a manifest.

Select your workspace, then paste the app manifest:

Slack App Manifest
{
    "_metadata": {
        "major_version": 2,
        "minor_version": 1
    },
    "display_information": {
        "name": "My Agent",
        "description": "AI Assistant — Powered by Claude Code",
        "background_color": "#1a1a2e"
    },
    "features": {
        "app_home": {
            "home_tab_enabled": false,
            "messages_tab_enabled": true,
            "messages_tab_read_only_enabled": false
        },
        "bot_user": {
            "display_name": "My Agent",
            "always_online": true
        },
        "slash_commands": [
            {
                "command": "/workers",
                "description": "List active and recent background workers",
                "should_escape": false
            },
            {
                "command": "/spawn",
                "description": "Spawn a background worker in this channel's workdir",
                "usage_hint": "<prompt>",
                "should_escape": false
            },
            {
                "command": "/context",
                "description": "Show this channel's workdir and context",
                "should_escape": false
            },
            {
                "command": "/clear",
                "description": "Clear conversation history for this channel",
                "should_escape": false
            },
            {
                "command": "/restart",
                "description": "Restart the Slack bot",
                "should_escape": false
            }
        ]
    },
    "oauth_config": {
        "scopes": {
            "bot": [
                "app_mentions:read",
                "channels:history",
                "channels:join",
                "channels:manage",
                "channels:read",
                "chat:write",
                "chat:write.public",
                "commands",
                "groups:history",
                "groups:read",
                "im:history",
                "im:read",
                "im:write",
                "mpim:history",
                "mpim:read",
                "users:read",
                "files:read",
                "files:write"
            ]
        }
    },
    "settings": {
        "event_subscriptions": {
            "bot_events": [
                "app_mention",
                "message.channels",
                "message.groups",
                "message.im",
                "message.mpim"
            ]
        },
        "interactivity": {
            "is_enabled": false
        },
        "org_deploy_enabled": false,
        "socket_mode_enabled": true,
        "token_rotation_enabled": false
    }
}

Update the display_name and name to your agent's name, then create the app.

10

Get Your Tokens

You need two tokens:

  • App Token (xapp-...): Go to Socket Mode → Enable → Generate token
  • Bot Token (xoxb-...): Go to OAuth & Permissions → Install to Workspace → Copy token
11

Configure and Start the Bot

Configure Slack bot
cd ~/agent-setup/shared/slack-bot
cp .env.example .env
nano .env
# Add your SLACK_BOT_TOKEN and SLACK_APP_TOKEN

npm install
node bot.js   # Test it first

Send a DM to your bot in Slack. If it responds, it's working!

12

Run as a Service

Set it up as a systemd service so it runs 24/7:

Set up systemd service
# Copy and edit the service file
cp ~/agent-setup/shared/slack-bot/agent-slack.service ~/.config/systemd/user/
nano ~/.config/systemd/user/agent-slack.service
# Update paths if your username isn't 'maia'

# Enable and start
systemctl --user daemon-reload
systemctl --user enable --now agent-slack

# Verify
systemctl --user status agent-slack

You're done!

Your AI assistant is live. Message it in Slack, set up cron jobs for automated tasks, and let it learn your preferences over time.

What's Next

Background Workers

Spawn long-running tasks that work in the background while you do other things. The heartbeat monitor checks them every 15 minutes and notifies you when they finish.

Spawn a worker
# From Slack:
/spawn Research the latest trends in AI agents and summarize findings

# From the command line:
~/.agent/scripts/spawn-worker.sh "Research AI trends" "Research the latest..." ~/

Custom Skills

Create skill files to give your agent domain-specific knowledge. Skills are markdown files that the agent reads when you invoke them with a slash command.

Create a custom skill
# Create a skill at ~/.claude/skills/my-skill.md
# Then reference it in your CLAUDE.md under the Skills section:
# - /my-skill — description of what it does

Multi-Channel Routing

Configure different Slack channels with their own workdirs and system prompts. Each channel can target a specific project or area.

config.json
// In config.json under slack.channels:
"dev-myproject": {
  "workdir": "~/myproject",
  "system_prompt": "You are working on MyProject. Always work in a feature branch."
}

Cron Jobs

Manage scheduled tasks with the cron CLI. Morning summaries, inbox triage, memory consolidation — your agent works while you sleep.

Manage cron jobs
# List all jobs:
~/.agent/cron/manage.sh list

# Enable the morning summary:
~/.agent/cron/manage.sh enable morning-summary
~/.agent/cron/manage.sh install

# Add a custom job:
~/.agent/cron/manage.sh add my-job "My Job" "0 9 * * 1-5" "America/New_York" "Do something useful" "~" true

Feedback System

Your agent automatically detects when you correct it and queues those corrections for review. Over time, corrections become persistent memories so the agent doesn't repeat the same mistakes. Review pending feedback anytime:

Review feedback
python3 ~/.agent/scripts/process-feedback.py
Questions? Check the GitHub repo for full documentation, or contact Madewell AI for help.
Created by Ben Valentin. Built at Madewell AI.