Skip to content

Repository files navigation

ModuStash

Simple, modular, stateful AI agent orchestration with sandboxed execution.

ModuStash is a self-hosted agent framework and workspace built with FastAPI, React, LangGraph, SQLite, FastMCP, and Temporal. It provides a dual-engine architecture: LangGraph for fast, micro-step reasoning and Temporal for long-running, durable macro-orchestration.


Core Philosophy & Goals

ModuStash was built around three primary principles:

  1. Simplicity First: Defining an agent should be as simple as writing a clear YAML manifest and a system prompt. Adding custom tools requires nothing more than placing standard Python functions with a single @register_tool decorator.
  2. Strict Modularity: Tools, model providers, memory limits, and project workspaces are completely decoupled.
    • Workspaces are execution boundaries, not capability providers. An agent only gets access to tools explicitly declared in its manifest.
  3. State & Isolation by Default: Every execution is strictly stateful and checkpointed using SQLite. Concurrent access to the same thread is protected with single-flight file-backed locking (ThreadBusyError). Filesystem operations are strictly sandboxed through descriptor-relative syscalls (dir_fd, O_NOFOLLOW).

Features

  • FastAPI REST and SSE backend.
  • React browser workspace served by FastAPI.
  • Strict, manifest-driven agent configuration.
  • Explicit per-agent tool capabilities.
  • Prompt-driven and native provider tool calling.
  • Descriptor-relative filesystem sandboxing.
  • Isolated project workspaces and artifact browsing.
  • SQLite-backed LangGraph checkpoints.
  • Composite checkpoint identities scoped by project, agent, and user thread.
  • Single-flight protection for active checkpoint threads.
  • Temporal workflows for durable long-running execution.
  • FastMCP gateway with explicit project and thread scoping.
  • Local CLI installed as modustash.
  • Loopback-only systemd services suitable for a private reverse proxy or Tailscale Serve.

Future Plans

This section tracks upcoming features and areas for community contribution:

  • Custom Topology Plugins: Support configurable agent topologies (such as ReAct, RSI wrappers, and Map-Reduce) declared directly inside YAML manifests.
  • Multi-Agent Swarm Orchestration: Direct agent-to-agent delegation protocols within the same project workspace.
  • Front-End polishing and wiring: General completion of frontend in the UI. Wrap up dead ends, etc.

Architecture

 ┌─────────────────────────────────────────┐
│ React Web UI / FastMCP / CLI │
└────────────────────┬────────────────────┘
│
▼
┌─────────────────────────┐
│ FastAPI Control Plane │
└────────────┬────────────┘
│
┌──────────────────────┴──────────────────────┐
▼ ▼
┌─────────────────────────┐ ┌─────────────────────────┐
│ LangGraph Micro-Loop │ │ Temporal Macro-Workflow │
│ (Step Reasoning & Tools)│ │ (Durable Timers/Loops) │
└────────────┬────────────┘ └────────────┬────────────┘
│ │
└──────────────────────┬──────────────────────┘
│
▼
┌────────────────────────────────────────────────────────┐
│ Core Sandbox & Memory │
│ • WorkspaceSandbox (dir_fd / O_NOFOLLOW file bounds) │
│ • SQLite Checkpointer (project:manifest:thread) │
│ • Token & Cost Budget Accumulator │
└────────────────────────────────────────────────────────┘

Agents own capabilities

Tools are granted by an agent manifest:

tools:
- name: read_workspace_filedescription: Read a UTF-8 workspace file.

A project workspace is only an execution boundary. Selecting a workspace does not grant filesystem, network, or other tools.

At graph compilation time, ModuStash intersects the manifest's declared tool names with the registered tool implementations. Only that intersection is bound to the model and graph nodes. A manifest that requests an unknown tool fails compilation.

The built-in tool modules are:

  • stash/tools/workspace_tools.py: project-bound filesystem tools.
  • stash/tools/http_tools.py: network HTTP tools.
  • stash/tools/registry.py: modular registry construction.

Prompt-driven tool protocol

Prompt-driven agents must emit the entire tool request as one clean fenced JSON block:

```json
{"tool": "read_workspace_file", "args": {"path": "notes.md"}}
```

Prose around the block, unfenced JSON, multiple objects, missing args, and extra object fields are rejected by the strict parser.

Checkpoint isolation

User-facing thread IDs are never used directly as LangGraph storage keys. Every checkpoint uses:

project:manifest_name:user_thread_id

For example:

default:workspace_assistant:session_123

The REST API, MCP gateway, CLI, and Temporal activities all use this composite identity. Concurrent executions against the same composite thread are rejected as Thread Busy; the REST API returns HTTP 429.

Workspace security

WorkspaceSandbox performs file access relative to open directory descriptors. It uses dir_fd, O_NOFOLLOW, regular-file checks, and atomic descriptor-relative replacement to prevent path traversal, symbolic-link escapes, and symlink retargeting races.

Uploads are limited to 26,214,400 bytes. Only UTF-8 text, Markdown, structured data, configuration files, and standard programming source extensions are accepted. Executable binaries, NUL-containing files, invalid UTF-8, and unknown file extensions are rejected.

Requirements

  • Python 3.11 or newer.
  • Node.js 18 or newer.
  • Git.
  • SQLite.
  • At least one API key for a model provider used by an enabled manifest.
  • Temporal CLI or an external Temporal server when durable workflows are used.

Supported model providers include OpenAI-compatible endpoints, Google Gemini, and Groq.

Local setup

Clone the repository and create a virtual environment:

git clone https://github.com/RY004/modustash.git
cd modustash
python3 -m venv .venv
source .venv/bin/activate
python -m pip install --upgrade pip
python -m pip install -e .

Create local configuration:

cp .env.example .env

Add the provider key required by your chosen manifest:

OPENAI_API_KEY=GOOGLE_API_KEY=GROQ_API_KEY=

Install and build the browser application:

npm --prefix frontend install
npm --prefix frontend run build

Start FastAPI:

uvicorn core.api:app --host 127.0.0.1 --port 8501 --reload

Open:

http://127.0.0.1:8501

For frontend development, run Vite separately:

npm --prefix frontend run dev

Vite listens on 127.0.0.1:5173 and proxies /api to FastAPI on port 8501.

CLI

Installing the project registers the modustash command.

Show help:

modustash --help

List and validate manifests:

modustash list
modustash list --json

Inspect compilation properties:

modustash inspect manifests/workspace_assistant.yaml

Run an agent:

modustash run \
manifests/workspace_assistant.yaml \
"List the workspace files and summarize them." \
--project default \
--thread-id local-session-1 \
--workspace ./projects/default \
--database ./data/database.sqlite

Reuse the same project, manifest, and thread ID to continue checkpointed state. Change any of those values to create an isolated checkpoint namespace.

Use --no-memory for an invocation without SQLite checkpoint persistence:

modustash run \
manifests/workspace_assistant.yaml \
"Create a short README draft." \
--no-memory

Temporal

Start a local Temporal development server on loopback:

temporal server start-dev \
--ip 127.0.0.1 \
--port 7233 \
--ui-port 8233 \
--db-filename ./data/temporal.db

Start the worker in another terminal:

source .venv/bin/activate
python -m temporal.worker

Relevant environment variables:

TEMPORAL_ADDRESS=127.0.0.1:7233TEMPORAL_NAMESPACE=defaultTEMPORAL_TASK_QUEUE=modustash-queueMODUSTASH_DATABASE_PATH=./data/database.sqliteMODUSTASH_PROJECTS_ROOT=./projectsMODUSTASH_MANIFESTS_ROOT=./manifests

Temporal workflow input supports an explicit project and thread_id. Activities convert those values and the validated manifest name into the same composite checkpoint identity used by the API and MCP gateway.

MCP gateway

Start the MCP server:

python -m core.mcp_server

It listens on:

http://127.0.0.1:8000/sse

Every agent MCP tool requires:

  • user_input
  • thread_id
  • project

There is no shared default MCP thread. State is scoped by project, manifest, and caller-supplied thread ID.

Browser interface

The React interface provides:

  • Project creation and selection.
  • Conversation sessions.
  • Agent selection from the active manifest registry.
  • Streaming model output over SSE.
  • Tool execution traces.
  • Token, cost, and step telemetry.
  • Workspace artifact listing and preview.
  • Validated text/source uploads.
  • Runtime and systemd service status.

Production builds are written to frontend/dist and served by FastAPI.

Systemd deployment

The included units run:

UnitPurposeListener
temporal-dev.serviceLocal Temporal server and UI127.0.0.1:7233, 127.0.0.1:8233
modustash-worker.serviceTemporal workerNo public listener
modustash-mcp.serviceFastMCP SSE gateway127.0.0.1:8000
modustash-ui.serviceFastAPI and React application127.0.0.1:8501

Install the deployment on a supported Debian or Ubuntu system:

bash deploy/install.sh

Review the installer and units before using them on a production or shared host.

Check service status:

sudo systemctl status \
temporal-dev.service \
modustash-worker.service \
modustash-mcp.service \
modustash-ui.service

Follow logs:

sudo journalctl \
-u temporal-dev.service \
-u modustash-worker.service \
-u modustash-mcp.service \
-u modustash-ui.service \
-f

Environment configuration

Start from .env.example.

Important variables include:

MODUSTASH_DATABASE_PATH=./data/database.sqliteMODUSTASH_PROJECTS_ROOT=./projectsMODUSTASH_MANIFESTS_ROOT=./manifestsTEMPORAL_ADDRESS=127.0.0.1:7233TEMPORAL_NAMESPACE=defaultTEMPORAL_TASK_QUEUE=modustash-queueOPENAI_API_KEY=GOOGLE_API_KEY=GROQ_API_KEY=

Never commit .env or provider credentials.

Security guidance

  • Keep all application listeners on loopback.
  • Place authentication and TLS in a trusted reverse proxy or Tailscale.
  • Review every manifest before enabling it.
  • Grant only the tools an agent requires.
  • Treat network tools as sensitive capabilities.
  • Keep project directories owned by the service account.
  • Do not replace project directories with symbolic links.
  • Restrict .env to mode 0600.
  • Encrypt backups containing checkpoints, prompts, uploads, and artifacts.
  • Back up SQLite databases using SQLite-aware tooling or stop writers first.
  • Keep Python, Node.js, Temporal, and operating-system dependencies updated.

License

ModuStash is distributed under the Apache License 2.0.


Crafting Agent Manifests

Agent manifests are strictly validated YAML files in manifests/.

Manifest Guidelines

  1. Explicit Tool Declaration: Manifests specify only the tools the agent is permitted to use. Selecting a project workspace does NOT grant tools by default.
  2. Explicit Budgets: Always set conservative step and USD budget ceilings to prevent infinite reasoning loops or runaway model bills.
  3. Choice of Tool Mode:
    • prompt_driven: Recommended when using open-weights models or providers without native function-calling support. Requires strict single fenced JSON responses.
    • native_api: Recommended for OpenAI, Gemini, or Groq function-calling capabilities.

Manifest Schema Reference

schema_version: "3.2"name: "my_custom_agent"description: "Brief summary of what this agent does."tool_execution_mode: "prompt_driven"# or "native_api"execution_engine: "langgraph"durability_engine: "temporal"# or "none"# Define inline or reference an external file via system_prompt_filesystem_prompt: | You are an expert file analyst. Always inspect files before drawing conclusions.models:
default:
provider: "openai"# openai, google, or groqmodel: "gpt-4o-mini"temperature: 0.1pricing:
input_cost_per_1m: 0.15output_cost_per_1m: 0.60tools:
- name: "read_workspace_file"description: "Read a UTF-8 file using a workspace-relative path."
- name: "search_workspace_file"description: "Search for a query string in a file."execution:
max_steps: 12max_token_budget: 50000max_usd_budget: 0.25recursion_limit: 32tool_error_policy: "return_to_model"memory:
checkpoint_backend: "sqlite"checkpoint_database: "database.sqlite"workspace_root: "projects/default"

Best Practices for System Prompts

When using prompt_driven execution mode, include explicit instructions in your system prompt detailing how tool calls should be formatted.

Rules to Enforce in System Prompts:

  1. Relative Paths Only: Instruct the model never to pass absolute paths (e.g., /etc/passwd, C:\...) or parent traversal sequences (..).
  2. One Tool at a Time: In prompt_driven mode, ModuStash parses one fenced JSON block per turn. Tell the model not to provide surrounding text or multiple JSON blocks when making a tool call.
  3. Honesty on Tool Output: Ensure the prompt instructs the model to rely only on data returned by executed tools and never to fake tool responses.

State & Memory Management

  • Composite Key Structure: All checkpoints are keyed as project:manifest_name:user_thread_id. If you want an agent to retain memory across requests, maintain the exact same thread_id and project.
  • Single-Flight Lock: ModuStash locks threads while execution is underway. If a user or API client sends concurrent requests for the same composite key, HTTP 429 (Thread Busy) will be returned.
  • Trimming Messages: Built-in context trimming uses standard LangChain strategies (strategy="last") starting on human messages to respect max_context_tokens.

Demonstration Agent (Simple Chatbot)

Included in the repo are the files needed for a simple, lightweight demonstration chatbot agent: manifests/demo_chatbot.yaml


How to Test the Demonstration Agent

1. Validate the new manifest via CLI

modustash inspect manifests/demo_chatbot.yaml

2. Run a query via CLI

modustash run manifests/demo_chatbot.yaml "Hello! Explain what ModuStash is in two sentences."

3. Test multi-turn stateful memory via CLI

Run the first message:

modustash run manifests/demo_chatbot.yaml "Hi, my favorite color is teal." --thread-id demo-chat-1

Follow up in the same thread:

modustash run manifests/demo_chatbot.yaml "What is my favorite color?" --thread-id demo-chat-1

4. Run via Web UI / FastAPI

When you start FastAPI (uvicorn core.api:app --port 8501), demo_chatbot will automatically appear in the agent selector dropdown in the browser UI.

About

ModuStash is a simple project, centered around modularity when building agents. The system also pairs well with a t3.micro or similar cloud instance, allowing for seamless implementation of long running agents and state persistence. This is a developing project and new features will be rolling out in the future! Enjoy :)

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Contributors

Languages