Your AI agent. On your hardware. Under your control.
Gino is a self-hosted AI agent written in Go. One binary, minimal dependencies, runs on a Raspberry Pi or a $5 VPS. It connects to any OpenAI-compatible LLM (OpenRouter, OpenAI, z.ai, Ollama, etc.) and works with Telegram and Discord.
The built-in knowledge brain uses Ollama for local embeddings — automatically bundled in the Docker image, so it works out of the box with zero configuration. Prefer to use your own infrastructure? Point Gino at any external Ollama instance, or disable the brain entirely. Your choice, your hardware.
Tiny footprint. The entire agent is a single ~12MB binary. Idle RAM usage is around 10MB. Cold start is instant — no runtime to spin up, no garbage to collect.
Advanced memory system. Gino doesn't just remember things — it understands them. A built-in SQLite knowledge brain provides hybrid search (FTS5 keyword + vector semantic similarity via Reciprocal Rank Fusion), an auto-extracted knowledge graph, and automatic context injection before every LLM call.
Signal system. Gino's unique Unix-domain-socket signal system lets external scripts, cron jobs, MCP servers, and IoT devices trigger pre-registered actions. Your camera detects motion? Send a signal. A build finishes? Send a signal. The agent wakes, processes, and responds on the right channel — without exposing freeform prompt injection.
Supply chain security. Gino minimizes its dependency surface and vendors everything. No npm-style transitive dependency trees. The full source you're running sits in vendor/ — auditable, reproducible, and immune to upstream package tampering.
Fast Docker/Podman deployment. A single container includes both Gino and Ollama. Copy .env.example, set your API key and bot token, docker compose up -d. That's it.
Built for real hardware. Cross-compiles to any platform Go supports. First-class ARM64 support for Raspberry Pi. No Node.js, no Python, no 500MB container layers.
git clone https://github.com/wltechblog/gino.git
cd gino/docker
cp .env.example .env
# Edit .env — at minimum set OPENAI_API_KEY and a channel token
docker compose up -dThe container bundles Ollama for embeddings when the brain is enabled. See docker/README.md for all options.
If you already have Ollama running externally (or don't need the brain), use the slim image — it's ~200MB lighter:
cd gino/docker
cp .env.example .env
# Set OLLAMA_URL to your external instance
docker compose -f docker-compose.slim.yml up -dThe slim image skips the Ollama download entirely. Set OLLAMA_URL in your .env to point at an external Ollama instance if you want brain features.
git clone https://github.com/wltechblog/gino.git
cd gino
make build
./gino onboard # creates ~/.gino with config + workspaceEdit ~/.gino/config.json with your API key and channel tokens, then:
./gino gateway# Raspberry Pi (ARM64)
GOOS=linux GOARCH=arm64 CGO_ENABLED=0 go build -ldflags="-s -w" -o gino ./cmd/gino
# Linux VPS (AMD64)
GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -ldflags="-s -w" -o gino ./cmd/ginoWorks on any Linux with 256MB RAM. Copy the binary and run.
Gino includes a SQLite-backed knowledge system that gives your agent real memory.
Hybrid search — FTS5 full-text search and vector semantic similarity are merged via Reciprocal Rank Fusion (RRF). You get keyword exactness and semantic understanding in every query.
Knowledge graph — Entities and relationships are auto-extracted from [[wikilinks]], @mentions, and natural language patterns. The agent can answer "who works at X?" or "what is Y connected to?".
Automatic context — Before every LLM call, the brain searches for relevant context and injects it into the system prompt. The agent has the right information at the right time, automatically.
Content dedup — SHA-256 hashing prevents importing the same content twice.
Add to ~/.gino/config.json:
{
"brain": {
"enabled": true,
"embeddingModel": "nomic-embed-text"
}
}Or in Docker, set GINO_BRAIN_ENABLED=true in your .env.
The brain needs an embedding model. With bundled Ollama, it works out of the box. For native Ollama:
curl -fsSL https://ollama.com/install.sh | sh
ollama pull nomic-embed-textWhen enabled, the agent gets these tools:
| Tool | What it does |
|---|---|
brain_search |
Hybrid search across all ingested content |
brain_ingest |
Import files or directories into the knowledge base |
brain_entity |
Look up entities and their relationships in the knowledge graph |
brain_status |
Show brain statistics (pages, entities, embeddings) |
brain_maintain |
Backfill missing embeddings, prune orphaned data |
Gino can be triggered by external systems via a Unix domain socket. Signals are action-based — they carry a registered action name, not freeform instructions. This means external scripts can wake the agent safely without prompt injection risk.
- Register actions in
config.json(or let MCP servers self-declare them) - External scripts send a JSON signal to the socket
- The agent wakes, injects a safe pre-defined response, and processes it
{
"signals": {
"actions": {
"check_messages": {
"response": "Check your messages and summarize anything important.",
"silent": false
},
"motion_detected": {
"response": "Motion was detected by the security camera. Check the feed.",
"channel": "telegram",
"chatId": "123456789"
}
}
}
}# Send a signal from any script
echo '{"action":"motion_detected"}' | socat - UNIX-CONNECT:~/.gino/signal.sockMCP servers can self-declare actions at startup via the protocol handshake — no manual registration needed.
Gino supports Model Context Protocol servers. Add them to your config:
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/path"]
}
}
}MCP tools are automatically discovered and made available to the agent. MCP servers can also self-declare signal actions for the signal system.
Gino includes a built-in tool set:
| Tool | Description |
|---|---|
filesystem |
Read, write, edit, and list files in the workspace |
exec |
Execute shell commands |
web |
Fetch web content (with timeout, size limit, and content-type filtering) |
message |
Send messages to the current channel |
write_memory / read_memory |
Persist and recall information |
cron |
Schedule one-time, recurring, or cron-expression tasks |
spawn |
Launch background subagents |
Gino has a powerful built-in scheduler with three modes:
- One-time — fire once after a delay
- Recurring — repeat at fixed intervals
- Cron expression — complex time-based rules with timezone support
Cron expressions use the standard 5-field syntax:
┌──────── minute (0-59)
│ ┌────── hour (0-23)
│ │ ┌──── day of month (1-31)
│ │ │ ┌── month (1-12)
│ │ │ │ ┌ day of week (0-6, Sun=0)
│ │ │ │ │
* * * * *
Supported syntax: */15 (steps), 1-5 (ranges), 1,3,5 (lists).
Examples:
| Expression | Meaning |
|---|---|
*/15 9-16 * * 1-5 |
Every 15 min during market hours, weekdays |
0 8 * * 1-5 |
8:00 AM every weekday |
0 17 * * 1-5 |
5:00 PM every weekday |
0 9 * * 1 |
9:00 AM every Monday |
0 0 1 * * |
Midnight on the 1st of every month |
30 6 * * 1-5 |
6:30 AM every weekday |
All jobs persist across restarts. Cron jobs recompute their next fire time on reload.
Skills are reusable knowledge modules stored in ~/.gino/workspace/skills/. Each skill is a markdown file with instructions, examples, and procedures. The agent loads them automatically and uses them when relevant.
Create a skill:
# Gino can create skills for you — just ask!
# Or create manually:
mkdir -p ~/.gino/workspace/skills/deploy
cat > ~/.gino/workspace/skills/deploy/SKILL.md << 'EOF'
# Deploy Skill
## Deploy to Production
1. Run tests: `make test`
2. Build: `make build`
3. Deploy: `./deploy.sh production`
EOFGino has a layered memory system:
- Daily notes (
memory/YYYY-MM-DD.md) — ephemeral context for today - Long-term memory (
memory/MEMORY.md) — durable facts and preferences - Knowledge brain (
brain.db) — searchable, indexed, entity-aware
The agent automatically extracts important facts from conversations and saves them to memory. It checks existing memory before writing to avoid duplicates, and can edit or correct specific facts.
| Channel | Type | Status | Notes |
|---|---|---|---|
| Telegram | Bot API | ✅ Stable | MarkdownV2 formatting with automatic reserved-character escaping |
| Discord | Bot (discordgo) | ✅ Stable | Channel monitoring, rate limiting |
| CLI | stdin/stdout | ✅ Built-in |
All config lives in ~/.gino/config.json:
{
"providers": {
"openai": {
"apiKey": "your-key",
"baseURL": "https://openrouter.ai/api/v1"
}
},
"agents": {
"defaults": {
"model": "google/gemini-2.5-flash",
"maxTokens": 8192,
"maxToolIterations": 100
}
},
"channels": {
"telegram": {
"token": "your-bot-token",
"allowFrom": ["your-user-id"]
},
"discord": {
"token": "your-bot-token",
"allowFrom": ["your-user-id"],
"monitorChannels": ["123456789012345678"]
}
},
"brain": {
"enabled": true,
"embeddingModel": "nomic-embed-text"
}
}Run ./gino onboard to generate a starter config interactively.
By default, the Discord bot only responds to @mentions. You can configure specific channels where the bot responds to every message without requiring a mention — useful for dedicated bot channels or continuous conversations:
{
"channels": {
"discord": {
"token": "your-bot-token",
"monitorChannels": ["123456789012345678"]
}
}
}Monitored channels have their own persistent sessions (keyed on channel ID), reply directly in-channel (no threads), and are still subject to rate limiting and the user allowlist.
All config values can be overridden via environment variables (useful for Docker):
| Variable | Description |
|---|---|
GINO_MODEL |
LLM model identifier |
GINO_MAX_TOKENS |
Max response tokens |
GINO_MAX_TOOL_ITERATIONS |
Max tool call loops per message |
GINO_BRAIN_ENABLED |
Enable the knowledge brain |
GINO_BRAIN_EMBEDDING_MODEL |
Ollama embedding model name |
GINO_HOME |
Home directory path |
GINO_SIGNAL_SOCKET |
Signal socket path |
GINO_WEB_TIMEOUT_S |
Web tool timeout (default: 30) |
GINO_WEB_MAX_RESPONSE_BYTES |
Web tool response size limit (default: 1MB) |
GINO_WEB_USER_AGENT |
Web tool User-Agent string |
OPENAI_API_KEY |
LLM provider API key |
OPENAI_API_BASE |
LLM provider base URL |
TELEGRAM_BOT_TOKEN |
Telegram bot token |
TELEGRAM_ALLOW_FROM |
Comma-separated Telegram user IDs |
DISCORD_BOT_TOKEN |
Discord bot token |
DISCORD_ALLOW_FROM |
Comma-separated Discord user IDs |
MIT