Parlor - a private parlor for AI conversation
Project description
Parlor
A private parlor for AI conversation.
Self-hosted ChatGPT-style web UI and agentic CLI that connects to any OpenAI-compatible API.
Install with pip. Run locally. Own your data.
Quick Start • Web UI • CLI Chat • Themes • Config • Security • API
Why Parlor?
Parlor connects to any OpenAI-compatible endpoint --- your company's internal API, OpenAI, Azure, Ollama, LM Studio, or anything else that speaks the OpenAI protocol. Built to OWASP ASVS L1 standards because your conversations deserve real security, not security theater.
One command. No cloud. No telemetry. No compromise.
pip install parlor
Quick Start
1. Install
pip install parlor
2. Configure --- create ~/.parlor/config.yaml:
ai:
base_url: "https://your-ai-endpoint/v1"
api_key: "your-api-key"
model: "gpt-4"
3. Verify your connection:
parlor --test
4. Launch:
parlor # Web UI
parlor chat # Terminal CLI
Your browser opens to http://127.0.0.1:8080. That's it.
Web UI
Conversations
| Create, rename, search, delete | Full conversation lifecycle with double-click rename |
| Full-text search | FTS5-powered instant search across all messages and titles |
| Fork at any message | Branch a conversation into a new thread from any point |
| Rewind conversation | Roll back to any message --- optionally revert file changes made by AI tools via git checkout |
| Edit & regenerate | Edit any user message, all subsequent messages are deleted, AI regenerates from there |
| Export to Markdown | One-click download of any conversation as .md |
| Auto-titles | AI generates a title from your first message |
| Per-conversation model | Switch models mid-conversation from the top bar dropdown |
| Copy between databases | Duplicate an entire conversation (with messages + tool calls) to another database |
Projects
Group conversations under projects with custom system prompts and per-project model selection. Your coding project uses Claude with a developer prompt. Your writing project uses GPT-4 with an editorial voice. Each project is its own world.
- Project-scoped system prompt overrides the global default
- Per-project model override (or "use global default")
- Project-scoped folders --- each project gets its own folder hierarchy
- Deleting a project preserves its conversations (they become unlinked, not deleted)
- "All Conversations" view to see everything across projects
Organization
|
Folders
|
Tags
|
Shared Databases
Connect multiple SQLite databases for team or topic-based separation. Each database is fully independent --- its own conversations, attachments, and history.
- Visual file browser with directory navigation for selecting
.db/.sqlite/.sqlite3files - Copy conversations between databases (full message + tool call history)
- Switch databases from the sidebar --- active database is visually indicated
- Database names: letters, numbers, hyphens, underscores only
- "personal" database always exists and can't be removed
- Paths restricted to your home directory for security
Rich Rendering
| Format | Support |
|---|---|
| Markdown | Full GFM --- tables, lists, blockquotes, strikethrough, task lists |
| Code blocks | Syntax highlighting via highlight.js with language label + one-click copy button |
| LaTeX math | Inline $x^2$ / \(x^2\) and display $$\int$$ / \[\int\] via KaTeX |
| Images | Inline previews for attached images |
| HTML subset | <kbd>, <sup>, <sub>, <dl>/<dt>/<dd> via DOMPurify allowlist |
File Attachments
Drag-and-drop or click to attach. 35+ file types supported. Up to 10 files per message, 10 MB each. Every file is verified with magic-byte detection --- a renamed .exe won't sneak through as a .png.
| Category | Extensions |
|---|---|
| Code | .py .js .ts .java .c .cpp .h .hpp .rs .go .rb .php .sh .bat .ps1 .sql .css |
| Data | .json .yaml .yml .csv .xml .toml .ini .cfg .log |
| Documents | .txt .md .pdf |
| Images | .png .jpg .jpeg .gif .webp |
- Image attachments show inline thumbnails with file size
- Non-image files force-download (never rendered in-browser)
- Filenames are sanitized: path components stripped, special characters replaced
MCP Tool Integration
Connect stdio or SSE-based MCP servers. Your AI gains access to external tools --- databases, APIs, file systems, anything with an MCP adapter.
- Tool calls render as expandable detail panels --- see input during execution, output + status when complete
- Spinner animation while tools execute
- Connected server count and total tool count shown in sidebar footer
- SSRF protection with DNS resolution and shell metacharacter rejection on tool args
mcp_servers:
- name: "my-tools"
transport: "stdio"
command: "npx"
args: ["-y", "@my-org/mcp-tools"]
env:
API_KEY: "${MY_API_KEY}"
DEBUG: "true"
- name: "remote-tools"
transport: "sse"
url: "https://mcp-server.example.com/sse"
Environment variables in env values support ${VAR} expansion --- so you can reference secrets from your shell environment without hardcoding them in the config file.
Streaming & Prompt Queue
Real-time token-by-token streaming via Server-Sent Events, with prompt queuing so you never have to wait.
- Type while streaming --- submit new messages while the AI is responding. They queue (up to 10) and process in FIFO order when the current response finishes
- Markdown and math render live as tokens arrive
- Raw mode toggle (eye icon in top bar) --- view unprocessed text during streaming, persists across sessions
- Stop generation mid-response with
Escapeor the stop button - Animated thinking indicator with pulsing dots while AI processes
- Error messages show inline with a Retry button
- Parallel tool execution --- when the AI calls multiple tools in one response, they run concurrently via
asyncio.as_completedinstead of sequentially
Command Palette
Cmd+K / Ctrl+K opens a Raycast-style command palette with fuzzy matching.
| Command type | What it does |
|---|---|
| New Chat | Create a fresh conversation |
| Theme: Midnight / Dawn / Aurora / Ember | Switch themes instantly |
| Model names | Switch the current model (all available models listed) |
| Project names | Jump to a project |
| Recent conversations | Quick-jump to your 10 most recent chats |
Arrow keys to navigate, Enter to select, Escape to dismiss.
Web UI Keyboard Shortcuts
| Shortcut | Action |
|---|---|
Cmd/Ctrl + K |
Open command palette |
Ctrl + Shift + N |
New conversation |
Escape |
Stop generation / close palette / close modal |
Enter |
Send message |
Shift + Enter |
Newline in message input |
Settings UI
Click the gear icon in the sidebar to open the settings modal:
- Model selector --- dropdown populated live from your API
- System prompt editor --- change at runtime, persists to
config.yaml - Theme picker --- visual cards showing each theme's color palette
- Changes take effect immediately, no restart needed
Themes
Four built-in themes, each with a distinct visual identity. Switch instantly via settings or command palette (Cmd+K).
Midnight Default --- Premium tech dark. Deep navy-charcoal with electric blue accents. Glassmorphic sidebar.
Dawn Light --- Warm editorial light. Cream backgrounds, soft indigo-violet accents, subtle paper texture.
Aurora Showstopper --- Living gradient dark with animated CSS aurora (purple/teal/emerald). Gradient borders, animated input focus rings.
Ember Cozy --- Warm luxury dark. Amber by firelight. Brown-charcoal backgrounds, rich amber glow on focus states.
Visual details:
- Glassmorphism with
backdrop-filter: blur(20px)on sidebar - Multi-layered shadows for depth:
0 1px 2px+0 4px 12px - Micro-animations: sidebar items shift on hover, buttons glow, modals spring in
- Gradient text effect on welcome heading
- Smooth 0.5s cross-fade transition between themes
- Code block copy button fades in on hover
- Theme persists in localStorage across sessions --- no flash on reload
Responsive Design
| Breakpoint | Target | Behavior |
|---|---|---|
| 1400px+ | Large desktop | Wider messages (900px), expanded sidebar (300px) |
| 769-1399px | Desktop | Default layout |
| 768-1024px | Tablet | Compact sidebar (240px), full-width messages |
| 0-767px | Mobile | Slide-over sidebar with hamburger menu + dark overlay |
Mobile sidebar slides in with transform animation. Tap the overlay or hamburger to dismiss.
CLI Chat Mode
An agentic terminal REPL --- like Claude Code, but connected to your own API. Read files, write code, run commands, search your codebase, all from the terminal. Type while the AI is working --- messages queue and process in order. Works on macOS, Linux, and Windows.
parlor chat # Interactive REPL
parlor chat "explain main.py" # One-shot mode
parlor chat -c # Continue last conversation
parlor chat -r <id> # Resume a specific conversation
parlor chat -p /path/to/project # Set project root
parlor chat --no-tools # Disable built-in tools
How It Works
- You type a prompt at the
you>prompt (or pass one as a CLI argument for one-shot mode) - A thinking spinner with elapsed timer appears while the AI generates a response
- When the AI calls tools (file reads, bash commands, etc.), the spinner pauses and tool calls display inline with their arguments and results
- Once complete, the full response renders as Rich Markdown --- syntax-highlighted code blocks, tables, headers, lists, bold/italic, and more
- A context footer shows a color-coded progress bar, token usage, response size, elapsed time, and remaining headroom before auto-compact
The AI runs in an agentic loop: it can call tools, inspect results, call more tools, and continue reasoning --- up to 50 iterations per turn by default. Multiple tool calls in a single response execute in parallel for faster results. This means it can tackle multi-step tasks like "find and fix the bug in auth.py" or "add tests for the user service" autonomously, without you needing to intervene between steps.
Prompt queuing: You can type and submit messages while the AI is working. They queue (up to 10) and process in FIFO order when the current response finishes. The prompt stays active at the bottom of the terminal while output streams above it.
Welcome Banner
On startup, the REPL displays:
Parlor CLI - /path/to/your/project
Model: gpt-4 | Tools: 6 | Instructions: loaded | Branch: main
Type /help for commands, Ctrl+D to exit
- Model: The active AI model from your config
- Tools: Total count of available tools (built-in + MCP)
- Instructions: Whether a
PARLOR.mdfile was found and loaded - Branch: Current git branch (auto-detected, shown only inside a git repo)
Built-in Tools
Six tools ship out of the box (no MCP server required):
| Tool | What it does | Key parameters |
|---|---|---|
read_file |
Read file contents with line numbers | path, offset (1-based start line), limit (max lines). Output truncated at 100KB. |
write_file |
Create or overwrite files. Parent directories created automatically. | path, content. Returns bytes written. |
edit_file |
Exact string replacement. old_text must match exactly once (unique) or use replace_all=true. |
path, old_text, new_text, replace_all. |
bash |
Run shell commands. Returns stdout, stderr, and exit code. | command, timeout (default 120s, max 600s). Output truncated at 100KB. |
glob_files |
Find files matching a glob pattern. Results sorted by modification time (newest first). | pattern (e.g., **/*.py), path (search root). Max 500 results. |
grep |
Regex search across files with context lines and file-type filtering. | pattern, path, glob filter, context lines, case_insensitive. Max 200 matches, skips files over 5MB. |
All file tools resolve paths relative to the working directory (or accept absolute paths). Every tool returns structured JSON that the AI uses to inform its next action.
Tool Safety
Two layers of protection prevent accidental damage:
Destructive command confirmation --- The following patterns in bash commands trigger an interactive Proceed? [y/N] prompt before execution:
rm, rmdir, git push --force, git push -f, git reset --hard, git clean, git checkout ., drop table, drop database, truncate, > /dev/, chmod 777, kill -9
Path and command blocking --- Hardcoded blocks that cannot be bypassed:
- Blocked paths:
/etc/shadow,/etc/passwd,/etc/sudoers, and anything under/proc/,/sys/,/dev/(follows symlinks) - Blocked commands:
rm -rf /,mkfs,dd if=/dev/zero, fork bombs - Null byte injection: Rejected in all paths, commands, and glob patterns
File References
Reference files and directories directly in your prompt with @:
you> explain @src/main.py
you> what tests cover @src/auth/
you> compare @old.py and @new.py
you> review @"path with spaces/file.py"
| Syntax | What it does |
|---|---|
@file.py |
Inlines the full file contents wrapped in <file path="..."> tags (truncated at 100KB) |
@directory/ |
Inlines a listing of directory contents wrapped in <directory> tags (up to 200 entries, with / suffix for subdirectories) |
@"quoted path" |
Handles paths containing spaces (single or double quotes) |
Paths are resolved relative to the working directory. Absolute paths are also supported. If a path doesn't exist, the @reference is left as-is in the prompt.
Thinking Spinner
While the AI generates a response, a Rich Status spinner (animated dots) displays on stderr with an elapsed timer that updates every second:
⠋ Thinking... (3s)
When the AI pauses to execute tool calls, the spinner stops and tool call output appears. The spinner resumes when the AI starts generating again. Total elapsed time accumulates across all thinking segments and is reported in the context footer.
Rich Markdown Rendering
Responses are buffered silently (no streaming flicker), then rendered in full using Rich's Markdown engine. This gives you:
- Syntax-highlighted code blocks with language detection
- Tables, headers, bold/italic, lists, blockquotes
- Padded layout with breathing room (2-character indent on both sides)
Output goes to stdout (for piping/redirecting), while chrome (spinner, tool calls, context footer) goes to stderr.
Context Management
Parlor tracks token usage across the conversation using tiktoken (cl100k_base encoding) with a character-estimate fallback (1 token per 4 chars) when tiktoken is unavailable:
| Threshold | What happens |
|---|---|
| 80,000 tokens | Yellow warning: "Use /compact to free space" |
| 100,000 tokens | Auto-compact triggers automatically before the next prompt |
| 128,000 tokens | Effective context window ceiling |
The context footer after each response shows:
[====----------------] 12,340/128,000 tokens (10%) | response: 482 | 3.2s | 87,660 until auto-compact
- Progress bar with color coding: green (<50%), yellow (50-75%), red (>75%)
- Current token count / max context window
- Response token count for the current turn
- Total elapsed thinking time
- Remaining tokens until auto-compact fires
Compact
/compact (or auto-compact at 100K tokens) sends the full conversation history to the AI with a summarization prompt. The AI generates a concise summary preserving:
- Key decisions and conclusions
- File paths that were read, written, or edited
- Important code changes and their purpose
- Current task state
- Errors encountered and how they were resolved
Tool call outputs are truncated to 500 characters each in the summary prompt to keep it focused. After compacting, the message history is replaced with a single system message containing the summary. Token savings are reported:
Compacted 47 messages -> 1 messages
~32,140 -> ~890 tokens
Minimum 4 messages required to compact (prevents compacting a nearly-empty conversation).
Conversation Management
Every conversation is persisted to SQLite. The REPL provides several ways to navigate them:
| Command | Behavior |
|---|---|
/new |
Creates a new conversation and clears message history |
/last |
Loads the most recent conversation (by creation time) with all its messages |
/list |
Shows the 20 most recent conversations with title, message count, and truncated ID |
/resume 3 |
Resume by list number (from /list output) |
/resume a1b2c3d4... |
Resume by full conversation ID |
Recent conversations:
1. Fix auth middleware bug (12 msgs) a1b2c3d4...
2. Add user settings page (8 msgs) e5f6a7b8...
3. Refactor database layer (23 msgs) c9d0e1f2...
Use /resume <number> or /resume <id>
On startup with -c, the most recent conversation is loaded automatically. With -r <id>, a specific conversation is loaded.
Auto-Title Generation
After the first exchange in a new conversation, Parlor sends your prompt to the AI to generate a short, descriptive title. This title appears in /list, the web UI sidebar, and is stored in the database. Titles are generated asynchronously and silently --- failures are ignored.
Model Switching
Switch models mid-session without restarting:
you> /model gpt-4-turbo
Switched to model: gpt-4-turbo
you> /model
Current model: gpt-4-turbo
Usage: /model <model_name>
The new model applies to all subsequent turns in the current session. The conversation history carries over.
REPL Commands
| Command | Action |
|---|---|
/new |
Start a new conversation |
/last |
Resume the most recent conversation |
/list |
Show 20 most recent conversations with message counts |
/resume N |
Resume by list number or full conversation ID |
/rewind |
Rewind to a previous message, optionally undoing file changes via git |
/compact |
Summarize and compact message history to free context |
/model NAME |
Switch to a different model mid-session (omit NAME to see current) |
/tools |
List all available tools (built-in + MCP), sorted alphabetically |
/skills |
List available skills with descriptions and source (default/global/project) |
/help |
Show all commands, input syntax, and keyboard shortcuts |
/quit, /exit |
Exit the REPL |
Input & Prompt
The you> prompt is displayed in bright cyan bold for clear visual distinction from AI responses.
Multiline input: Enter submits your message. Alt+Enter inserts a newline. Continuation lines are indented to align under the prompt text.
Paste collapsing: When you paste more than 6 lines, the terminal display automatically collapses to show the first 3 lines plus ... (N more lines). The full content is still sent to the AI --- only the visual display is truncated to keep your terminal readable.
Keyboard Shortcuts
| Shortcut | Action |
|---|---|
Enter |
Submit message |
Alt+Enter |
Insert a newline (multiline input) |
Escape |
Cancel the current AI response |
Ctrl+C |
Clear current input. If input is empty, exit the REPL. During AI generation, also cancels. |
Ctrl+D |
Exit the REPL (EOF) |
Tab |
Autocomplete (see below) |
Tab Completion
Tab completion works for three categories:
- Commands: Type
/thenTabto see all slash commands (/new,/list,/compact, etc.) - Skills: Type
/thenTabto also see skill names (/commit,/review,/explain, and any custom skills) - File paths: Type
@thenTabto browse files and directories from the working directory. Subdirectories show with a/suffix. Hidden files (starting with.) are excluded. Supports nested path completion (@src/thenTab).
Command History
Input history is persisted to ~/.parlor/cli_history using prompt_toolkit's FileHistory. Previous commands are available with the up/down arrow keys across sessions.
One-Shot Mode
Pass a prompt as an argument to run a single turn and exit:
parlor chat "list all Python files in src/"
parlor chat "explain the auth middleware"
parlor chat -c "now add rate limiting to it" # Continue last conversation
parlor chat -r a1b2c3d4 "fix the failing test" # Continue specific conversation
One-shot mode creates a conversation in the database (visible in the web UI), generates a title, and exits after the response completes. The AI still has access to all tools and can run multiple agentic iterations --- the "one-shot" refers to a single user turn, not a single AI action.
Skills
Skills are reusable prompt templates invoked with /name. Three ship by default:
| Skill | What it does |
|---|---|
/commit |
Runs git diff, stages relevant files, creates a conventional commit (type(scope): description) |
/review |
Reviews git diff for bugs, security issues, performance concerns, missing error handling, and missing tests. Returns structured findings with severity levels. |
/explain |
Reads referenced code and explains architecture, data flow, components, and design patterns |
Skill arguments: Append extra context after the skill name. It gets appended to the skill's prompt template:
you> /commit
you> /review just the auth changes
you> /explain @src/services/agent_loop.py focus on the event system
Custom skills: Add YAML files to ~/.parlor/skills/ (global) or .parlor/skills/ (per-project):
name: test
description: Run tests and fix failures
prompt: |
Run the test suite. If any tests fail, read the failing test
and the relevant source code, then fix the issue.
name: deploy
description: Build and deploy to staging
prompt: |
1. Run the full test suite
2. Build the production bundle
3. Deploy to the staging environment
4. Verify the deployment is healthy
Precedence: Project skills (.parlor/skills/) override global skills (~/.parlor/skills/), which override default built-in skills. Parlor walks up from the working directory to find the nearest .parlor/skills/ directory, similar to how PARLOR.md is discovered.
Project Instructions (PARLOR.md)
Create a PARLOR.md in your project root to inject context into every conversation:
# Project: my-app
## Tech Stack
- Python 3.12, FastAPI, SQLAlchemy
- PostgreSQL 16, Redis 7
## Conventions
- All functions must have type hints
- Use conventional commits
- Tests required for all new features
Discovery: Parlor walks up from the current working directory to find the nearest PARLOR.md. A global ~/.parlor/PARLOR.md applies to all projects. Both are loaded if found --- global instructions come first, then project-specific ones.
System prompt construction: The CLI builds a system prompt from three sources, concatenated in this order:
- Working directory context:
"You are an AI coding assistant working in: /path/to/project"+ tool usage guidance PARLOR.mdinstructions (global + project, if found)- The
system_promptfromconfig.yaml
Shared Database
CLI and web UI share the same SQLite database (~/.parlor/chat.db). Conversations created in the terminal show up in the web sidebar, and vice versa. Every user message, assistant response, and tool call is persisted with the same schema used by the web UI.
Titles are auto-generated after the first exchange. Tool calls are stored with their inputs and outputs, so the web UI can render expandable tool call panels for conversations that originated in the CLI.
Unified Tool Executor
Built-in tools and MCP tools work side by side. When the AI calls a tool:
- Built-in tools are checked first --- if the tool name matches a registered built-in (
read_file,write_file, etc.), it executes locally with no network overhead - MCP tools are checked second --- if no built-in matches, the call is forwarded to the appropriate MCP server
This means a single conversation can use read_file (built-in) and query_database (MCP) in the same agentic loop. Use /tools to see the full list of available tools from both sources.
Cross-Platform Support
The CLI works on macOS, Linux, and Windows:
- Signal handling (Ctrl+C to cancel generation) uses
asyncio.add_signal_handleron Unix and falls back gracefully on Windows where async signal handlers aren't supported - Path resolution uses
os.path.realpathfor consistent behavior across platforms - The prompt toolkit input handles platform-specific terminal differences
CLI Configuration
Add a cli: section to ~/.parlor/config.yaml:
cli:
builtin_tools: true # Enable built-in file/bash tools (default: true)
max_tool_iterations: 50 # Max agentic loop iterations per response (default: 50)
builtin_tools: falsedisables all six built-in tools. MCP tools (if configured) still work. Same effect as--no-toolson the command line.max_tool_iterationscaps how many tool-call rounds the AI can make per turn. Set lower for simpler tasks, higher for complex multi-step operations.
MCP servers configured in the same config.yaml are available in both CLI and web UI.
Configuration
Config File
~/.parlor/config.yaml
ai:
base_url: "https://your-ai-endpoint/v1"
api_key: "your-api-key"
model: "gpt-4"
system_prompt: "You are a helpful assistant."
verify_ssl: true # set false for self-signed certs
app:
host: "127.0.0.1" # bind address
port: 8080 # server port
data_dir: "~/.parlor" # where DB + attachments live
tls: false # set true for HTTPS with self-signed cert
# Optional: CLI settings
cli:
builtin_tools: true # Enable built-in tools (default: true)
max_tool_iterations: 50 # Max tool calls per response (default: 50)
# Optional: shared databases
shared_databases:
- name: "team-shared"
path: "~/shared/team.db"
# Optional: MCP tool servers
mcp_servers:
- name: "my-tools"
transport: "stdio"
command: "npx"
args: ["-y", "@my-org/mcp-tools"]
env:
API_KEY: "${MY_API_KEY}"
- name: "remote-tools"
transport: "sse"
url: "https://mcp-server.example.com/sse"
Environment Variables
Every config option has an env var override:
| Variable | Default | Description |
|---|---|---|
AI_CHAT_BASE_URL |
--- | AI API endpoint (required) |
AI_CHAT_API_KEY |
--- | API key (required) |
AI_CHAT_MODEL |
gpt-4 |
Model name |
AI_CHAT_SYSTEM_PROMPT |
You are a helpful assistant. |
System prompt |
AI_CHAT_VERIFY_SSL |
true |
SSL certificate verification |
Command Reference
parlor Launch web UI server and open browser
parlor chat Interactive CLI chat (agentic REPL)
parlor chat "msg" One-shot mode (run prompt and exit)
parlor chat -c Continue last conversation
parlor chat -r ID Resume a specific conversation
parlor chat --no-tools Disable built-in tools
parlor --test Test connection, list models, send test prompt
parlor --help Show help
Example --test output
Config:
Endpoint: https://your-ai-endpoint/v1
Model: gpt-4
SSL: enabled
1. Listing models...
OK - 12 model(s) available
- gpt-4
- gpt-4-turbo
- gpt-3.5-turbo
...
2. Sending test prompt to gpt-4...
OK - Response: Hello! How can I help you today?
All checks passed.
Security
Parlor is hardened for use on corporate networks and shared machines. Not a checkbox exercise --- real, layered defense.
| Layer | What it does |
|---|---|
| Authentication | Random session token, HttpOnly cookies, HMAC-SHA256 timing-safe comparison |
| CSRF | Per-session tokens validated on all state-changing requests |
| CSP | script-src 'self', frame-ancestors 'none', no inline scripts |
| Security Headers | X-Frame-Options DENY, X-Content-Type-Options nosniff, strict Referrer-Policy, Permissions-Policy |
| Database | Column-allowlisted SQL builder, parameterized queries everywhere, 0600 file permissions, path validation |
| Input Sanitization | DOMPurify on all rendered HTML, UUID validation on all IDs, filename sanitization |
| Rate Limiting | 120 req/min per IP with LRU eviction |
| Body Size | 15 MB max request |
| CORS | Locked to configured origin, explicit method/header allowlist |
| File Safety | MIME type allowlist + magic-byte verification, path traversal prevention, forced download for non-images |
| MCP Safety | SSRF protection with DNS resolution, shell metacharacter rejection in tool args |
| SRI | SHA-384 hashes on all vendor scripts |
| API Surface | OpenAPI/Swagger docs disabled |
| CLI Safety | Destructive command confirmation, path validation blocks /etc/shadow, /proc/, etc. |
Full details in SECURITY.md.
Data Storage
Everything stays on your machine. Nothing phones home.
~/.parlor/
config.yaml # Configuration (permissions: 0600)
chat.db # SQLite + WAL journal (permissions: 0600)
cli_history # REPL command history
attachments/ # Files by conversation (permissions: 0700)
The data directory is created with 0700 permissions (owner-only). Database files are created with 0600 permissions. WAL and SHM sidecar files are locked down too.
API Reference
Parlor exposes a full REST API. All endpoints require authentication via session cookie + CSRF token.
Conversations
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/conversations |
List (with ?search=, ?project_id=, ?db=) |
POST |
/api/conversations |
Create |
GET |
/api/conversations/:id |
Get with messages, attachments, and tool calls |
PATCH |
/api/conversations/:id |
Update title, folder, model |
DELETE |
/api/conversations/:id |
Delete with all attachments |
GET |
/api/conversations/:id/export |
Export as Markdown |
POST |
/api/conversations/:id/chat |
Stream chat (SSE) |
POST |
/api/conversations/:id/stop |
Cancel active generation |
POST |
/api/conversations/:id/fork |
Fork at a message position |
POST |
/api/conversations/:id/rewind |
Rewind to a position, optionally revert file changes |
POST |
/api/conversations/:id/copy |
Copy to another database (?target_db=) |
Messages & Attachments
| Method | Endpoint | Description |
|---|---|---|
PUT |
/api/messages/:id |
Edit message content (deletes subsequent messages) |
DELETE |
/api/messages/:id |
Delete messages after a position |
GET |
/api/attachments/:id |
Download attachment file |
Projects
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/projects |
List all projects |
POST |
/api/projects |
Create project (name, instructions, model) |
PATCH |
/api/projects/:id |
Update name, instructions, or model |
DELETE |
/api/projects/:id |
Delete project (conversations preserved) |
Folders
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/folders |
List folders (?project_id= to filter) |
POST |
/api/folders |
Create folder (name, parent_id, project_id) |
PATCH |
/api/folders/:id |
Update name, parent, collapsed state, position |
DELETE |
/api/folders/:id |
Delete folder + subfolders (conversations preserved) |
Tags
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/tags |
List all tags |
POST |
/api/tags |
Create tag (name, color) |
PATCH |
/api/tags/:id |
Update name or color |
DELETE |
/api/tags/:id |
Delete tag (removed from all conversations) |
POST |
/api/conversations/:id/tags/:tag_id |
Add tag to conversation |
DELETE |
/api/conversations/:id/tags/:tag_id |
Remove tag from conversation |
Databases
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/databases |
List all connected databases |
POST |
/api/databases |
Add database (name, path) |
DELETE |
/api/databases/:name |
Remove database connection |
GET |
/api/browse?path= |
Browse filesystem for .db/.sqlite/.sqlite3 files |
Config & Models
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/config |
Get current config + MCP server statuses |
PATCH |
/api/config |
Update model and/or system prompt |
POST |
/api/config/validate |
Test API connection, list models |
GET |
/api/models |
List available models (sorted) |
GET |
/api/mcp/tools |
List all available MCP tools with schemas |
Development
git clone https://github.com/troylar/parlor.git
cd parlor
pip install -e ".[dev]"
pytest tests/ -v # Run 343 tests
ruff check src/ tests/ # Lint
ruff format src/ tests/ # Format
Tech Stack
| Backend | Python 3.10+, FastAPI, Uvicorn |
| Frontend | Vanilla JS (no build step), marked.js, highlight.js, KaTeX, DOMPurify |
| CLI | Rich, prompt-toolkit, tiktoken |
| Database | SQLite with FTS5 full-text search, WAL journaling |
| AI | OpenAI Python SDK (async streaming) |
| MCP | Model Context Protocol SDK (stdio + SSE transports) |
| Streaming | Server-Sent Events (SSE) |
| Typography | Inter + JetBrains Mono (self-hosted WOFF2, zero external requests) |
| Security | OWASP ASVS L1 compliance, SRI, CSP, CSRF, rate limiting |
MIT License
Built for people who care about their conversations.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file parlor-0.11.0.tar.gz.
File metadata
- Download URL: parlor-0.11.0.tar.gz
- Upload date:
- Size: 706.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6d56dc860fd8ade1bcf043e0513c9208bfc00e366ef095bcd71ece40efda817e
|
|
| MD5 |
27f4b82ddf4cc63ecb38d0dba8b409e8
|
|
| BLAKE2b-256 |
ea4eb7dd8227ab580aeb4c158e2065711f76af01af5c547493a90b20b678d0a7
|
File details
Details for the file parlor-0.11.0-py3-none-any.whl.
File metadata
- Download URL: parlor-0.11.0-py3-none-any.whl
- Upload date:
- Size: 698.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
64b87e2674f19f0d25aff74099eb93ca5c382f0602239a68835c0b8979d12aa6
|
|
| MD5 |
21eb6099cccd42a72bc4858b35a8d3cf
|
|
| BLAKE2b-256 |
21eb512263a7df2c38e27cf09dc6b87310370e1ece68bc5d48684d1c14c115a5
|