A wiki and document store built with React and Express.
Single-user personal knowledge base - A full-stack TypeScript application that provides a beautiful, intuitive interface for creating and managing markdown documents organized in a hierarchical folder structure.
⚠️ Note: Codex is designed as a single-user application. It does not support concurrent multi-user editing or collaboration features. Perfect for personal wikis, note-taking, and documentation.
- 📁 Folder Management: Create, delete, and rename folders in a collapsible tree view with right-click context menus
- 📝 Markdown Pages: Create and edit markdown documents with GitHub Flavored Markdown support
- 🎨 Three-Pane Layout: Folder tree (left), markdown editor (center), live preview (right)
- 📐 Fully Resizable: Drag to resize both horizontal panes (left/right) and vertical sections (folder tree/page list)
- 🌓 Theme Options: Auto-detects system theme preference with manual override - cycles through auto/light/dark/high-contrast modes
- ♿ Accessibility: Full ARIA labels, semantic HTML, high-contrast theme, and improved color contrast for WCAG compliance
- 📤 Move Pages: Elegant folder picker to move pages between folders via right-click menu
- 💾 Smart Auto-save: 10-second throttled saves with 5-second typing debounce - prevents excessive saves while keeping your work safe
- 🔄 Live Preview: Real-time markdown preview that updates instantly as you type (no waiting for saves)
- 🔗 Internal Links: Click links to other
.mdfiles to navigate within the app; anchor links scroll to headings - 📜 Version History: Git-backed version control with visual diff highlighting (green/red for additions/deletions)
- 🔍 Restore Versions: Browse and restore any previous version of your documents
- 📖 Reading Mode: Open any page in a new window for distraction-free reading
- 🔃 Synchronized Scrolling: Editor scroll position syncs to preview pane
- 📄 Auto-select README: Navigating to a folder automatically opens its README.md if present
- 🔎 Full-Text Search: Quick search across all pages with keyboard shortcut (⌘K/Ctrl+K) and relevance-ranked results
- 📑 Table of Contents: Auto-generated, collapsible TOC for easy document navigation with active section highlighting
- 🎤 Speech-to-Text: Dictate content using Web Speech API (Chrome/Edge/Safari)
- ⚡ Performance Caching: Server-side caching layer with 30-second TTL for fast folder/page loading
- 🌐 RESTful API: Programmatic access to all folder and page operations
- ✅ Tested: Comprehensive test suite for both backend and frontend
- 🎯 Collapsible Panes: Hide sidebars for distraction-free writing
- 🚀 Fast & Lightweight: Built with Vite for lightning-fast development
- 🔐 Password Protection: Simple password-based authentication to secure your data
- 🛡️ Security Features: Rate limiting, request logging, and security headers
Codex includes several security features to protect your data:
- Password-based login with bcrypt hashing (10 salt rounds)
- Session management using httpOnly cookies (24-hour expiration)
- Authentication can be disabled by not setting
AUTH_PASSWORD(not recommended for public deployments)
- Login endpoint is rate-limited to 5 attempts per 15 minutes per IP address
- Prevents brute force password attacks
- Returns
429 Too Many Requestswhen limit is exceeded
All login attempts are logged with timestamps and IP addresses:
✓ Successful login from 192.168.1.100- Successful authentication✗ Failed login attempt from 192.168.1.100- Invalid passwordLogin attempt without password from 192.168.1.100- Missing passwordLogin attempt when auth disabled from 192.168.1.100- Auth not configured
HTTP request logging (via morgan):
- Development: Concise colored output showing method, URL, status, and response time
- Production: Combined Apache-style logs with full details
Helmet middleware provides:
- Content Security Policy (CSP)
- X-Frame-Options (clickjacking protection)
- X-Content-Type-Options (MIME sniffing protection)
- Strict-Transport-Security (HTTPS enforcement in production)
- And other security headers
- Always set a strong
AUTH_PASSWORDfor deployments - Use a unique
SESSION_SECRETin production - Enable HTTPS in production (
NODE_ENV=production) - Monitor logs for suspicious login patterns
- Consider deploying behind a reverse proxy (nginx, Caddy) for additional security
Codex is designed to be accessible to all users, including those using assistive technologies:
- Comprehensive ARIA labels on all interactive elements
- Semantic HTML structure using
<header>,<nav>,<main>,<section>,<aside>, and<article>elements - Live regions (
aria-live) announce dynamic content updates - Proper roles (
role="tree",role="button",role="dialog") for enhanced navigation - Keyboard navigation support with proper focus management and
tabIndexattributes - Descriptive labels explain the state and purpose of all controls
- Four theme options: Auto (follows system), Light, Dark, and High-Contrast
- High-contrast mode provides maximum visual clarity:
- Pure black (#000) background with white (#fff) text
- Yellow (#ffff00) secondary text for clear distinction
- Cyan (#00ffff) accent colors for links and interactive elements
- White borders for clear element separation
- Improved contrast ratios in all themes for WCAG compliance
- Larger interactive elements: Buttons sized at 32px for easier clicking
- Consistent theming: All features including reading mode support all themes
- Tab navigation through all interactive elements
- Arrow key navigation (↑↓) or vim-style (
j/k) in folder tree, page list, and search results - Enter to activate buttons, open folders/pages, and select search results
- Escape to close modals and dialogs
- ⌘K/Ctrl+K global search shortcut
- Focus indicators show keyboard-selected items with blue outline
- Mouse hover sync updates keyboard selection for seamless interaction
The accessibility features ensure Codex can be used effectively by people with:
- Visual impairments (screen readers, high-contrast mode)
- Motor disabilities (keyboard-only navigation, larger click targets)
- Color blindness (semantic colors with sufficient contrast)
# Clone the repository
git clone https://github.com/bocan/codex.git
cd codex
# Install dependencies
make installCreate a .env file in the root directory:
# Required: Set your password
AUTH_PASSWORD=your-secure-password-here
# Optional: Server port (default: 3001)
PORT=3001# Start both server and client
make dev
# Visit http://localhost:3000
# Login with your AUTH_PASSWORD# Development
make dev # Run both server and client
make dev-server # Run server only (port 3001)
make dev-client # Run client only (port 3000)
# Building
make build # Build both server and client
make build-server # Build server only
make build-client # Build client only
# Testing
make test # Run all tests
make test-server # Run backend tests
make test-client # Run frontend tests
# Maintenance
make clean # Remove all node_modules and build artifacts
make install # Fresh install of all dependencies
make help # Show all available commands- Make your changes in the appropriate files
- Tests run automatically in watch mode (if enabled)
- Server auto-reloads on file changes (via ts-node-dev)
- Client hot-reloads on file changes (via Vite HMR)
- Run
make testbefore committing
This project uses Conventional Commits and commit-and-tag-version for automated changelog generation.
type(scope): description
Examples:
feat: add search functionality
fix: resolve dark mode flicker
docs: update README
refactor(api): simplify error handling
Types: feat, fix, docs, style, refactor, perf, test, build, ci, chore
# 1. Create and switch to feature branch
git switch -c feature/my-change
# 2. Make changes, commit (repeat as needed)
git add .
git commit -m "feat: add new feature"
# 3. Push branch to GitHub
git push -u origin feature/my-change
# 4. Create PR (via GitHub CLI or web)
gh pr create --title "feat: add new feature" --body "Description"
# 5. After PR is merged, switch back to main
git switch main
git pullAfter merging PRs and when ready to release:
# Patch release (1.0.0 → 1.0.1) - bug fixes
npm run release
# Minor release (1.0.0 → 1.1.0) - new features
npm run release:minor
# Major release (1.0.0 → 2.0.0) - breaking changes
npm run release:major
# Push with tags
git push --follow-tagsThis will:
- Bump version in
package.json - Update
CHANGELOG.mdwith commits since last release - Create a git commit and tag (e.g.,
v1.1.0)
- Create controller in
server/src/controllers/ - Add route in
server/src/routes/ - Register route in
server/src/index.ts - Add tests in
server/tests/
- Create component in
client/src/components/ - Add corresponding CSS file
- Import and use in parent component
- Add tests in same directory (
.test.tsx)
codex/
├── 📄 Makefile # Build automation
├── 📄 package.json # Root package config
├── 📄 README.md # This file
│
├── 📁 server/ # Backend (Express + TypeScript)
│ ├── 📄 package.json # Server dependencies
│ ├── 📄 tsconfig.json # TypeScript config
│ ├── 📄 jest.config.js # Test configuration
│ ├── 📁 src/
│ │ ├── 📄 index.ts # Server entry point
│ │ ├── 📁 controllers/ # Request handlers
│ │ │ ├── folderController.ts
│ │ │ └── pageController.ts
│ │ ├── 📁 routes/ # API routes
│ │ │ ├── folders.ts
│ │ │ └── pages.ts
│ │ └── 📁 services/ # Business logic
│ │ └── fileSystem.ts # File operations
│ └── 📁 tests/ # Test files
│ └── api.test.ts
│
├── 📁 client/ # Frontend (React + TypeScript)
│ ├── 📄 package.json # Client dependencies
│ ├── 📄 tsconfig.json # TypeScript config
│ ├── 📄 vite.config.ts # Vite configuration
│ ├── 📄 vitest.config.ts # Test configuration
│ ├── 📄 index.html # HTML entry point
│ ├── 📁 src/
│ │ ├── 📄 main.tsx # React entry point
│ │ ├── 📄 App.tsx # Main app component
│ │ ├── 📄 App.css # App styles
│ │ ├── 📁 components/ # React components
│ │ │ ├── FolderTree.tsx # Folder navigation
│ │ │ ├── FolderTree.css
│ │ │ ├── PageList.tsx # Page list in folder
│ │ │ ├── PageList.css
│ │ │ ├── Editor.tsx # Markdown editor
│ │ │ ├── Editor.css
│ │ │ ├── Preview.tsx # Markdown preview
│ │ │ ├── Preview.css
│ │ │ └── *.test.tsx # Component tests
│ │ ├── 📁 services/ # API client
│ │ │ └── api.ts
│ │ ├── 📁 types/ # TypeScript types
│ │ │ └── index.ts
│ │ └── 📁 test/ # Test setup
│ │ └── setup.ts
│
└── 📁 data/ # File storage
└── Welcome.md # Default welcome page
- Quick Start
- Installation
- Usage
- Project Structure
- MCP Server (AI Agent Access)
- API Documentation
- Testing
- Development
- Tech Stack
- Makefile Commands
- Troubleshooting
- Contributing
The fastest way to get started using the Makefile:
# Install all dependencies
make install
# Run the application
make devThen open http://localhost:3000 in your browser!
- Node.js 18+ and npm
- macOS, Linux, or Windows with WSL
make installnpm run install:all# Install root dependencies
npm install
# Install server dependencies
cd server && npm install
# Install client dependencies
cd ../client && npm install# Run both server and client in development mode
make dev
# Or run them separately
make dev-server # Runs server on port 3001
make dev-client # Runs client on port 3000# Run both concurrently
npm run dev
# Or run separately
npm run dev:server
npm run dev:clientThe application will be available at:
- Frontend: http://localhost:3000
- Backend API: http://localhost:3001
- API Health Check: http://localhost:3001/api/health
- Navigate Folders: Click on folders in the left pane to select them
- Create Folders: Right-click any folder and select "New Folder"
- Create Pages: Select a folder and click "+ New Page"
- Edit Content: Click on a page to open it in the editor (center pane)
- Rename Page: Right-click on a page and select "Rename"
- Move Page: Right-click on a page and select "Move to..." to move it to another folder
- Delete Page: Right-click on a page and select "Delete"
- Preview: See live markdown preview in the right pane
- Auto-save: Content saves automatically after 2 seconds
- Resize Panes:
- Drag the edge of left/right panes to resize horizontally
- Drag the divider between folder tree and page list to resize vertically
- Collapse Panes: Use the arrow buttons to hide left/right panes
- Theme Toggle: Click the theme button (🌓/☀️/🌙/◐) in the header to cycle through auto/light/dark/high-contrast modes
# Using Make
make build
# Using npm
npm run buildThis creates optimized production builds in:
server/dist/- Compiled backendclient/dist/- Optimized frontend bundle
Codex can be run in Docker for simplified deployment. In production mode, Express serves both the API and the React UI from a single port (3001).
Official multi-architecture (AMD64/ARM64) images are available on Docker Hub:
docker pull bocan/codex:latestAvailable tags:
latest- Latest stable release2.7.0- Specific version (example)2.7- Minor version (example)2- Major version (example)
Image verification (signed with Cosign):
cosign verify bocan/codex:latest \
--certificate-identity-regexp=https://github.com/bocan/codex \
--certificate-oidc-issuer=https://token.actions.githubusercontent.comSupply chain security:
- Images are signed with Cosign for authenticity verification
- SBOM (Software Bill of Materials) embedded in image metadata
- Provenance attestations document the build process
To inspect the SBOM:
docker buildx imagetools inspect bocan/codex:latest --format "{{ json .SBOM }}"# Build and start the container
docker compose up -d --build
# View logs
docker compose logs -f codex
# Stop the container
docker compose downThe application will be available at http://localhost:3001 (both UI and API on the same port).
# Build the image
docker build -t codex .
# Run the container
docker run -d \
--name codex \
-p 3001:3001 \
-v $(pwd)/data:/app/data \
-e NODE_ENV=production \
codex
# View logs
docker logs -f codex
# Stop and remove
docker stop codex && docker rm codexEnvironment Variables (in docker-compose.yml):
NODE_ENV=production- Runs in production modePORT=3001- Server port (default)AUTH_PASSWORD=your-password- Set to enable authentication (optional)TRUST_PROXY=true- Enable when behind a reverse proxy with HTTPS
Volume Mounting:
./data:/app/data- Persists your wiki data outside the container
Important: Due to secure cookie requirements, there are two ways to run Codex with authentication:
Remove or comment out the AUTH_PASSWORD environment variable in docker-compose.yml:
environment:
- NODE_ENV=production
- PORT=3001
# - AUTH_PASSWORD=your-password # Commented outThis allows direct HTTP access without authentication, suitable for local testing.
For production with authentication, deploy behind a reverse proxy (nginx, Caddy, Traefik) that:
- Terminates TLS/HTTPS
- Forwards requests to Codex with the
X-Forwarded-Proto: httpsheader - Set
TRUST_PROXY=truein the environment variables
Example with nginx:
server {
listen 443 ssl;
server_name wiki.example.com;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
location / {
proxy_pass http://localhost:3001;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}Why? In production mode, session cookies use secure: auto, which requires HTTPS. Direct HTTP access with a password set will fail because the browser won't send the secure cookie. The reverse proxy provides HTTPS termination while communicating with Codex via HTTP internally.
Codex includes a Model Context Protocol (MCP) server that allows AI agents like Claude, GitHub Copilot, and other MCP-compatible clients to interact with your documentation.
-
Enable the MCP server by setting environment variables:
export MCP_ENABLED=true export MCP_API_KEY=your-secure-api-key
-
Start the server (runs alongside the main app):
npm run dev:mcp -w server # Development with hot reload -
Connect your MCP client to
http://localhost:3002/mcp
The MCP server exposes 12 tools for AI agents:
| Tool | Description |
|---|---|
search_pages |
Search documentation by query |
get_page |
Read a page's content |
create_page |
Create a new page |
update_page |
Update an existing page |
delete_page |
Delete a page |
rename_page |
Rename a page |
move_page |
Move a page to another folder |
list_folders |
Get folder hierarchy |
list_pages |
List pages in a folder |
create_folder |
Create a new folder |
delete_folder |
Delete an empty folder |
rename_folder |
Rename a folder |
Claude Desktop (~/Library/Application Support/Claude/claude_desktop_config.json):
{
"mcpServers": {
"codex": {
"url": "http://localhost:3002/mcp",
"headers": { "Authorization": "Bearer your-api-key" }
}
}
}VS Code / GitHub Copilot (settings or mcp.json):
{
"servers": {
"codex": {
"type": "http",
"url": "http://localhost:3002/mcp",
"headers": { "Authorization": "Bearer your-api-key" }
}
}
}For full documentation, see server/src/mcp/README.md.
The REST API is available at http://localhost:3001/api
GET /api/healthResponse:
{
"status": "ok"
}GET /api/foldersResponse:
{
"name": "root",
"path": "/",
"type": "folder",
"children": [
{
"name": "Projects",
"path": "Projects",
"type": "folder",
"children": []
}
]
}POST /api/folders
Content-Type: application/json
{
"path": "folder-name"
}
# or nested: "parent/subfolder"Example:
curl -X POST http://localhost:3001/api/folders \
-H "Content-Type: application/json" \
-d '{"path": "My Notes"}'DELETE /api/folders/:pathExample:
curl -X DELETE http://localhost:3001/api/folders/My%20NotesPUT /api/folders/rename
Content-Type: application/json
{
"oldPath": "old-name",
"newPath": "new-name"
}Example:
curl -X PUT http://localhost:3001/api/folders/rename \
-H "Content-Type: application/json" \
-d '{"oldPath": "My Notes", "newPath": "Work Notes"}'GET /api/pages?folder=folder-pathResponse:
[
{
"name": "page1.md",
"path": "folder/page1.md",
"type": "file"
}
]GET /api/pages/:pathResponse:
{
"path": "folder/page1.md",
"content": "# Page Title\n\nContent here..."
}Example:
curl http://localhost:3001/api/pages/My%20Notes/hello.mdPOST /api/pages
Content-Type: application/json
{
"path": "folder/page.md",
"content": "# Page Title\n\nContent here..."
}Example:
curl -X POST http://localhost:3001/api/pages \
-H "Content-Type: application/json" \
-d '{"path": "My Notes/hello.md", "content": "# Hello World\n\nThis is my first page!"}'PUT /api/pages/:path
Content-Type: application/json
{
"content": "# Updated Content"
}Example:
curl -X PUT http://localhost:3001/api/pages/My%20Notes/hello.md \
-H "Content-Type: application/json" \
-d '{"content": "# Updated Hello\n\nNew content!"}'DELETE /api/pages/:pathExample:
curl -X DELETE http://localhost:3001/api/pages/My%20Notes/hello.mdPUT /api/pages/rename/file
Content-Type: application/json
{
"oldPath": "old-page.md",
"newPath": "new-page.md"
}Example:
curl -X PUT http://localhost:3001/api/pages/rename/file \
-H "Content-Type: application/json" \
-d '{"oldPath": "My Notes/hello.md", "newPath": "My Notes/welcome.md"}'PUT /api/pages/move
Content-Type: application/json
{
"oldPath": "folder1/page.md",
"newFolderPath": "folder2"
}
# Returns: { "success": true, "newPath": "folder2/page.md" }Example:
curl -X PUT http://localhost:3001/api/pages/move \
-H "Content-Type: application/json" \
-d '{"oldPath": "My Notes/hello.md", "newFolderPath": "Projects"}'# Using Make
make test
# Using npm
npm test# Backend tests only
make test-server
# or: cd server && npm test
# Frontend tests only
make test-client
# or: cd client && npm test
# Frontend tests with UI
cd client && npm run test:ui
# Backend tests in watch mode
cd server && npm run test:watchThe project includes tests for:
- ✅ All API endpoints (folders and pages)
- ✅ File system operations
- ✅ React components (FolderTree, Editor, Preview)
- ✅ Error handling and edge cases
If you see "Port 3000 or 3001 already in use":
# Find and kill process on port 3001
lsof -ti:3001 | xargs kill -9
# Find and kill process on port 3000
lsof -ti:3000 | xargs kill -9# Clean everything and reinstall
make clean
make install
# Or manually
rm -rf node_modules server/node_modules client/node_modules
npm install
cd server && npm install
cd ../client && npm install# Rebuild TypeScript projects
cd server && npm run build
cd ../client && npm run build# Make sure dependencies are installed
make install
# Run tests with verbose output
cd server && npm test -- --verbose
cd client && npm test -- --verboseThe data/ directory is created automatically. If you encounter permission issues:
# Make sure the data directory is writable
chmod 755 data/- Check the GitHub Issues
- Review the API documentation above
- Ensure all prerequisites are installed
- Try a clean install:
make clean && make install
| Shortcut | Description |
|---|---|
⌘K / Ctrl+K |
Open search modal |
↑ ↓ |
Navigate search results |
Enter |
Select highlighted search result |
Esc |
Close search modal |
| Shortcut | Description |
|---|---|
↑ / k |
Move selection up (vim-style) |
↓ / j |
Move selection down (vim-style) |
Enter |
Open selected folder or page |
Note: Click in the folder tree or page list to focus it, then use keyboard navigation. Mouse hover also updates the keyboard selection for seamless interaction.
Codex includes a powerful full-text search that:
- Searches across all pages in all folders recursively
- Ranks results by relevance (number of matches)
- Highlights matching text in context snippets
- Shows search context (50 characters before and after match)
- Keyboard-driven with quick access via
⌘K/Ctrl+K - Instant results with 300ms debounce for smooth typing
- Arrow key navigation to browse results
- Click or Enter to jump to matching pages
- Click the search button in the header or press
⌘K/Ctrl+K - Type your query (case-insensitive)
- Navigate results with
↑↓arrow keys - Press
Enteror click to open the page - Press
Escto close the search modal
The Table of Contents automatically:
- Extracts all headings from the current document
- Creates hierarchical structure based on heading levels (H1-H6)
- Highlights the current section as you scroll
- Provides quick navigation - click any heading to jump to it
- Collapsible sidebar - click the toggle to show/hide
- Remembers your preference - state persists across sessions
- Smooth scrolling to target sections
- Auto-hides on small screens (responsive design)
The TOC appears as a floating widget on the right side of the preview pane when viewing pages with multiple headings.
- Express
^4.18.2- Fast, unopinionated web framework - TypeScript
^5.3.3- Type-safe JavaScript - CORS
^2.8.5- Cross-origin resource sharing - ts-node-dev
^2.0.0- Development server with auto-reload
Testing:
- Jest
^29.7.0- Testing framework - Supertest
^6.3.3- HTTP assertion library - ts-jest
^29.1.1- TypeScript preprocessor for Jest
- React
^18.2.0- UI library - TypeScript
^5.3.3- Type-safe JavaScript - Vite
^5.0.11- Next-generation frontend tooling - React Markdown
^9.0.1- Markdown renderer - remark-gfm
^4.0.0- GitHub Flavored Markdown - Axios
^1.6.5- HTTP client
Testing:
- Vitest
^1.1.3- Unit test framework - Testing Library - React testing utilities
- jsdom
^23.2.0- DOM implementation
- Concurrently
^8.2.2- Run multiple commands - ESLint - Code linting
- Prettier - Code formatting (optional)
All available Make commands:
make help # Show all available commands
make install # Install all dependencies
make dev # Run both server and client in dev mode
make dev-server # Run server only
make dev-client # Run client only
make build # Build both server and client
make build-server # Build server only
make build-client # Build client only
make test # Run all tests
make test-server # Run server tests
make test-client # Run client tests
make clean # Remove node_modules and build artifacts
make clean-server # Clean server only
make clean-client # Clean client only# Quick start
make install && make dev
# Clean slate
make clean && make install && make test
# Production build
make clean && make install && make buildContributions are welcome! Here's how you can help:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Make your changes
- Run tests (
make test) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
- Use TypeScript for all new code
- Follow existing code formatting
- Add tests for new features
- Update documentation as needed
MIT
- Built with React and Express
- Markdown rendering by react-markdown
- Icons from Unicode emoji
Made with ❤️ for taking better notes