"All they need is
toolsshell commands!"
A personal AI agent that runs on your machine, talks to a local LLM (or any OpenAI-compatible endpoint), and gets things done - from chat conversations and scheduled tasks to file processing and Telegram integration.
Palim runs an LLM-powered agent inside a sandboxed shell. Instead of implementing custom tool functions for every capability, the agent interacts with its environment through shell commands.
You define a working directory where the agent has full read/write access. This directory is mounted into a virtual filesystem powered by just-bash. Extensions and skills expose their functionality as shell programs, keeping the interface uniform and composable.
- Quick Start
- Highlights
- Scope
- Extensions
- Configuration
- Current State
- Development
- Production
- Docker / Podman
- Architecture
- Tech Stack
- Contributing
- License
Prerequisites: Bun 1.3.14+ and any OpenAI-compatible LLM endpoint.
bun install # Install dependencies
bun run setup # Interactive first-time configuration
bun run start # Start PalimThe setup script creates your .env, asks for your LLM endpoint and API key, generates the admin password, and builds the frontend. The admin credentials are printed at the end - once running, open http://localhost:3000 and log in with them.
After first start, check Settings > Extensions in the web UI to enable the capabilities you want (optional extensions are disabled by default).
- Conversational AI - Multi-turn sessions with streamed responses and persistent chat history
- Local LLM support - Use any inference engine with an OpenAI-compatible API (llama.cpp, llama-swap, vLLM, etc.)
- Extension system - 13 built-in extensions covering webhooks, scheduling, Telegram, workflows, wiki, MCP bridging, ntfy notifications, and more
- Sandboxed execution - The agent's shell runs inside a virtual filesystem where only
AGENT_WORK_DIRis mounted - file operations are real, but the agent cannot access anything outside that directory
- Local-first AI - Designed and tested around local LLMs (e.g. via llama.cpp or llama-swap proxy), though any OpenAI-compatible endpoint works
- Shell-driven actions - Every non-trivial operation is exposed as a shell command inside the sandboxed workspace, giving the LLM full autonomy to interact with the system
- No direct secret access - Secrets are not visible to agents. Extensions and shell commands handle credentials on its behalf. (Note: secrets may still appear in job logs if extensions log them.)
- System and information integration - Palim is not intended to be a software development agent. While it can technically do this, its main focus is integrating systems, automating workflows, and managing information
Palim ships with 13 built-in extensions (8 optional, 5 core):
| Extension | Type | Purpose |
|---|---|---|
filewatcher |
Core | Configurable directory watchers that emit events on file changes |
scheduler |
Core | Cron and interval-based job scheduling with persistence |
webhooks |
Core | Authenticated HTTP endpoints for receiving external service events |
workflows |
Core | DAG job pipelines defined in JSON5 (parallel steps, joins, control flow, {{...}} template expressions with function calls) |
core-wf-steps |
Core | Built-in workflow step types (HTTP request, fail, chunk, start-workflow) |
converter |
Optional | Converts files (PDFs, images) to markdown via vision LLM |
error-analyzer |
Optional | Automatic failure analysis and error reporting for jobs and workflows |
mcp |
Optional | Bridges MCP (Model Context Protocol) servers into the skill system |
ntfy |
Optional | Sends push notifications via ntfy.sh, exposed as a workflow step type |
steering |
Optional | Injects additional system prompt text to steer agent behavior |
telegram |
Optional | Telegram bot integration |
web-fetch |
Optional | Fetch and read webpages (replacement for curl, wget, etc.) |
wiki |
Optional | Agent skill for reading and writing wiki pages |
Core extensions cannot be deactivated. Optional extensions are disabled by default on first setup - enable the ones you need in the web UI under Settings > Extensions.
For writing your own extensions, see docs/writing-extensions.md.
| Variable | Purpose | Default |
|---|---|---|
OPENAI_API_KEY |
LLM provider API key | - |
OPENAI_API_BASE_URL |
LLM endpoint base URL | http://localhost:11434/v1 |
OPENAI_DEFAULT_MODEL |
LLM default model | - |
AGENT_WORK_DIR |
Agent working directory (mounted into the sandbox) | .work/ |
WEB_HOST |
Web server bind address | localhost |
WEB_PORT |
Web server port | 3000 |
AUTH_ADMIN_USER |
Username of the admin account seeded on first boot | admin |
AUTH_ADMIN_PASSWORD |
Password for the seeded admin (empty = generate a one-time password and log it once) | - |
AUTH_SESSION_TTL_MS |
Login token lifetime in ms | 604800000 (7 days) |
DATA_DIR |
Directory for databases and generated content | <AGENT_WORK_DIR>/.palim/ |
EXTENSIONS_DIR |
Custom extensions directory | src/extensions |
SECRETS_MASTER_KEY |
Derivation key for SecretVault encryption | - |
Palim reads secrets (API keys, tokens) from environment variables. Set them in your .env file and you're done.
For runtime secrets used by extensions and workflows, Palim provides a SQLite-backed encrypted vault (AES-256-GCM) with per-row ACL and audit logging. Extensions access secrets via ctx.secrets.get(key) / ctx.secrets.set(key, value), and workflows use {{secret.KEY_NAME}} template syntax. The vault requires SECRETS_MASTER_KEY (or derivation from .env.keys) for encryption. For production deployments with encrypted environment files, Palim optionally supports dotenvx - this activates automatically when a .env.keys file is present. See docs/secrets.md for the full secrets architecture and docs/api-security-model.md for the API authentication model.
For non-sensitive shared configuration, Palim also has a plaintext global variable store (no encryption, no ACL) managed in the web UI. Workflows reference these via {{var.KEY_NAME}} template syntax.
- Local LLMs - Tested with models in the 20B-35B parameter range (Qwen 3.6, Gemma 4, gpt-oss). Smaller models (like Qwen 3.6 4B) work but may struggle with complex multi-turn tasks. Enabling thinking mode is recommended for most models.
- Extensions - Functional with a stable SDK (
src/extensions/sdk.ts). The API surface is still evolving. - Workflows - DAG-based engine with parallel fan-out, join barriers, and control flow. String fields support
{{...}}template expressions with composable built-in function calls (e.g.{{ jsonEscape(stripDataUri(image.dataUrl)) }}). Visual DAG editing in the web UI is functional but still being refined.
Available bun run commands:
bun run dev # Backend with file watching
cd frontend && bun run dev # Frontend dev server (Vite HMR)
bun run check # Lint and format (Biome)
bun run test # Run tests
bun run cli # Interactive sandbox shell (just-bash)bun run dev starts the backend with auto-restart on file changes. bun run start runs without watching (for production-like usage).
Remote access during development
To access the Vite dev server from another machine on your network:
cd frontend && bunx vite --host 0.0.0.0Build the frontend and run the backend:
bun install --frozen-lockfile
cd frontend && bun install --frozen-lockfile && bun run build
bun run startRecommendations:
- Set
AUTH_ADMIN_PASSWORDbefore first boot (or note the generated one-time password in the log) and change it after logging in. All API and WebSocket endpoints require a user login; see docs/api-security-model.md for the full security model - If you lose admin access, run
bun run reset-admin [username]on the host - Use a reverse proxy (nginx, Caddy) for TLS termination
A multi-stage Dockerfile and docker-compose.yml are provided for containerized deployment. Works with both Docker and Podman.
bun run setup # Create .env (if you haven't already)
docker build -t palim .
docker compose upSee docs/docker.md for networking setup, local LLM connectivity, volumes, and Podman-specific notes.
src/
├── app/ # Bootstrap and lifecycle orchestration
├── db/ # Drizzle ORM + SQLite (schema, migrations, config store)
├── session/ # Conversation session persistence
├── jobs/ # Job queue definitions (agent, chat)
├── queue/ # ManagedQueue abstraction over bunqueue
├── web/ # Elysia HTTP server, WebSocket, REST routes
├── extensions/ # Plugin system (13 built-in extensions)
│ └── core/ # Non-deactivatable infrastructure extensions
├── secrets/ # Encrypted vault, ACL, audit logging
├── variables/ # Plaintext global variable store (no ACL, no encryption)
├── skills/ # Skill loading and YAML frontmatter parsing
├── tools/ # Agent tools (file ops, sandbox shell)
└── utils/ # Shared utilities (logging, validation, commands)
shared/ # Types shared between backend and frontend
frontend/ # Svelte 5 + Vite 8 + Tailwind CSS 4 web UI
.work/ # Default AGENT_WORK_DIR - the agent's workspace (tasks, inbox, wiki, workflows)
Key architectural decisions:
- Sandboxed workspace - Commands run inside a just-bash virtual filesystem. The directory configured via
AGENT_WORK_DIRis mounted into this sandbox, giving the agent full access to that directory while isolating it from the rest of the host filesystem - Job queues - SQLite-backed via bunqueue with stall detection and persistent logging
- Extensions - Discovered at runtime, dependency-resolved via topological sort, each receiving a scoped context
- Skills - Markdown files with YAML frontmatter; the agent reads them at runtime via
skill read <name>in the sandbox shell - Sessions - Persisted multi-turn conversations in SQLite with source tracking (chat, telegram, scheduler)
| Layer | Technology |
|---|---|
| Runtime | Bun 1.3.14+ |
| Language | TypeScript 7.0 (strict mode) |
| Frontend | Svelte 5 + Vite 8 + Tailwind CSS 4 |
| Database | SQLite (Bun native) via Drizzle ORM |
| LLM | OpenAI-compatible API |
| Sandbox | just-bash |
| Queue | bunqueue |
See CONTRIBUTING.md for setup instructions, code style guidelines, testing conventions, and PR expectations.

