A flat-file kanban board framework built with PHP 8.1+, vanilla JavaScript, and JSON storage. No database required.
- Kanban boards — Organize work into columns and cards
- Drag and drop — Reorder cards and columns intuitively
- Labels & due dates — Categorize and track deadlines
- Column emojis — Add visual identifiers to columns
- Board colors — Customize board background colors
- Public share links — Share boards publicly with unique URLs (read-only)
- Dark/light mode — Automatic based on system preference
- Responsive design — Works on desktop and mobile
- Multi-user authentication — Role-based access with admin and read-only roles
- Board-level permissions — Control which users can access specific boards
- Workspace visibility — Make workspace public or require login
- Environment configuration — All settings via
.envfile - Flat-file storage — No database required, JSON files only
- PHP 8.1 or later
- Composer
CardStack is designed as a framework that you clone and customize. Your board data lives in the data/ directory which is gitignored, allowing you to pull updates from upstream without conflicts.
# Clone the repository
git clone https://github.com/albrightlabs/cardstack-core.git my-boards
cd my-boards
# Install dependencies
composer install
# Configure your instance
cp .env.example .env
# Edit .env with your settings (app name, colors, admin password, etc.)
# Start the development server
php -S localhost:8000 -t public public/router.phpIf you want to maintain your own instance while still being able to pull framework updates:
# Clone the framework
git clone https://github.com/albrightlabs/cardstack-core.git my-company-boards
cd my-company-boards
# Rename origin to upstream (framework)
git remote rename origin upstream
# Add your own repository as origin
git remote add origin https://github.com/your-org/your-boards.git
# Push to your instance repo
git push -u origin mainSince the data/ directory and custom assets are gitignored, you can pull updates from the upstream repository:
git fetch upstream
git merge upstream/main
composer installYour boards and customizations (in .env, custom.css, custom.js) remain untouched.
All configuration is done via the .env file:
| Variable | Description | Default |
|---|---|---|
APP_URL |
Base URL (no trailing slash) | http://localhost:8000 |
ADMIN_PASSWORD |
Password for admin access | changeme |
DATA_PATH |
Path to data storage | ./data |
SITE_NAME |
Site name in header/title | CardStack |
SITE_EMOJI |
Emoji next to site name | 📋 |
LOGO_URL |
Custom logo image URL | (empty) |
LOGO_WIDTH |
Max logo width in pixels | 120 |
FAVICON_URL |
Custom favicon URL | (empty) |
FAVICON_EMOJI |
Favicon emoji | (uses SITE_EMOJI) |
FAVICON_LETTER |
Letter overlay on favicon | (first letter of name) |
FAVICON_SHOW_LETTER |
Show letter on favicon | true |
Create public/assets/custom.css for additional styling:
/* Override the accent color */
:root {
--accent-color: #8b5cf6;
}
/* Custom header background */
.site-header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}Create public/assets/custom.js for custom behavior:
document.addEventListener('DOMContentLoaded', () => {
console.log('Custom scripts loaded');
});Both files are gitignored in the framework, so your customizations won't conflict with upstream updates.
cardstack-core/
├── data/ # Board data (gitignored except _example/)
│ ├── _example/ # Example boards (included in framework)
│ └── boards/ # Your board JSON files
├── public/ # Web root
│ ├── index.php # Front controller
│ ├── api.php # API entry point
│ ├── router.php # Dev server router
│ └── assets/
│ ├── style.css # Framework styles
│ ├── app.js # Core JavaScript
│ ├── board.js # Board interactions
│ ├── custom.css # Your styles (gitignored)
│ └── custom.js # Your scripts (gitignored)
├── src/ # PHP application code
│ ├── Config.php # Configuration
│ ├── Auth.php # Authentication
│ ├── Board.php # Board operations
│ ├── Column.php # Column operations
│ ├── Card.php # Card operations
│ ├── Api.php # API routing
│ └── helpers.php # Utilities
├── templates/ # PHP templates
│ ├── layout.php # Main layout
│ ├── boards.php # Board list
│ ├── board.php # Single board
│ ├── login.php # Login form
│ └── 404.php # Not found
├── .env # Your config (gitignored)
├── .env.example # Config template
├── composer.json
└── LICENSE # MIT License
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/boards |
List all boards |
POST |
/api/boards |
Create a board |
GET |
/api/boards/:id |
Get a board |
PUT |
/api/boards/:id |
Update a board |
DELETE |
/api/boards/:id |
Delete a board |
POST |
/api/boards/:id/columns |
Add a column |
PUT |
/api/boards/:id/columns/:id |
Update a column |
DELETE |
/api/boards/:id/columns/:id |
Delete a column |
PUT |
/api/boards/:id/columns/reorder |
Reorder columns |
POST |
/api/columns/:id/cards |
Add a card |
GET |
/api/cards/:id |
Get a card |
PUT |
/api/cards/:id |
Update a card |
DELETE |
/api/cards/:id |
Delete a card |
PUT |
/api/cards/:id/move |
Move a card |
GET |
/api/boards/:id/share |
Get share status |
POST |
/api/boards/:id/share |
Enable sharing |
DELETE |
/api/boards/:id/share |
Disable sharing |
POST |
/api/boards/:id/share/regenerate |
Regenerate share token |
GET |
/api/share/:token |
Get shared board (public) |
CardStack supports Trello-style public share links. When sharing is enabled, anyone with the link can view a board in read-only mode without needing to log in.
- Open a board and click the menu button (⋮)
- Click "Share Board" to open the share modal
- Toggle "Enable public link" to generate a share URL
- Copy and share the link (e.g.,
https://boards.example.com/s/abc123def456)
- Read-only access — Viewers cannot edit, drag, or modify anything
- No login required — Anyone with the link can view
- Regenerate links — Invalidate old links by generating a new token
- Disable anytime — Turn off sharing to make the board private again
Share links use the format /s/{token} where the token is a 12-character cryptographically secure string.
For detailed deployment instructions including Laravel Forge setup and data persistence strategies, see DEPLOYMENT.md.
- Session-based authentication with 2-hour timeout
- CSRF tokens required for all mutations
- Session ID regeneration on login
- HttpOnly, SameSite=Strict session cookies
- Input sanitization and validation
- No external dependencies (minimal attack surface)
MIT License — see LICENSE for details.