22 KiB
Project Structure Design
Purpose
This document defines the complete directory and file structure for the StarPunk project. It provides the exact layout that developer agents should create, including all directories, their purposes, file organization, and naming conventions.
Philosophy
The project structure follows these principles:
- Flat is better than nested: Avoid deep directory hierarchies
- Conventional is better than clever: Use standard Python project layout
- Obvious is better than hidden: Clear directory names over abbreviations
- Data is sacred: User data isolated in dedicated directory
Complete Directory Structure
starpunk/
├── .venv/ # Python virtual environment (gitignored)
├── .env # Environment configuration (gitignored)
├── .env.example # Configuration template (committed)
├── .gitignore # Git ignore rules
├── README.md # Project documentation
├── CLAUDE.MD # AI agent requirements document
├── LICENSE # Project license
├── requirements.txt # Python dependencies
├── requirements-dev.txt # Development dependencies
│
├── app.py # Main application entry point
│
├── starpunk/ # Application package
│ ├── __init__.py # Package initialization
│ ├── config.py # Configuration management
│ ├── database.py # Database operations
│ ├── models.py # Data models
│ ├── auth.py # Authentication logic
│ ├── micropub.py # Micropub endpoint
│ ├── feed.py # RSS feed generation
│ ├── notes.py # Note management
│ └── utils.py # Helper functions
│
├── static/ # Static assets
│ ├── css/
│ │ └── style.css # Main stylesheet (~200 lines)
│ └── js/
│ └── preview.js # Optional markdown preview
│
├── templates/ # Jinja2 templates
│ ├── base.html # Base layout template
│ ├── index.html # Homepage (note list)
│ ├── note.html # Single note view
│ ├── feed.xml # RSS feed template
│ └── admin/
│ ├── base.html # Admin base layout
│ ├── login.html # Login form
│ ├── dashboard.html # Admin dashboard
│ ├── new.html # Create note form
│ └── edit.html # Edit note form
│
├── data/ # User data directory (gitignored)
│ ├── notes/ # Markdown note files
│ │ ├── 2024/
│ │ │ ├── 11/
│ │ │ │ ├── first-note.md
│ │ │ │ └── second-note.md
│ │ │ └── 12/
│ │ │ └── december-note.md
│ │ └── 2025/
│ │ └── 01/
│ │ └── new-year.md
│ └── starpunk.db # SQLite database
│
├── tests/ # Test suite
│ ├── __init__.py
│ ├── conftest.py # Pytest configuration and fixtures
│ ├── test_auth.py # Authentication tests
│ ├── test_database.py # Database tests
│ ├── test_micropub.py # Micropub endpoint tests
│ ├── test_feed.py # RSS feed tests
│ ├── test_notes.py # Note management tests
│ └── test_utils.py # Utility function tests
│
└── docs/ # Architecture documentation
├── architecture/
│ ├── overview.md
│ ├── components.md
│ ├── data-flow.md
│ ├── security.md
│ ├── deployment.md
│ └── technology-stack.md
├── decisions/
│ ├── ADR-001-python-web-framework.md
│ ├── ADR-002-flask-extensions.md
│ ├── ADR-003-frontend-technology.md
│ ├── ADR-004-file-based-note-storage.md
│ ├── ADR-005-indielogin-authentication.md
│ └── ADR-006-python-virtual-environment-uv.md
├── design/
│ ├── project-structure.md (this file)
│ ├── database-schema.md
│ ├── api-contracts.md
│ ├── initial-files.md
│ └── component-interfaces.md
└── standards/
├── documentation-organization.md
├── python-coding-standards.md
├── development-setup.md
└── testing-strategy.md
Directory Purposes
Root Directory (/)
Purpose: Project root containing configuration and entry point.
Key Files:
app.py- Main Flask application (import app from starpunk package).env- Environment variables (NEVER commit).env.example- Template for configurationrequirements.txt- Production dependenciesrequirements-dev.txt- Development toolsREADME.md- User-facing documentationCLAUDE.MD- AI agent instructionsLICENSE- Open source license
Rationale: Flat root with minimal files makes project easy to understand at a glance.
Application Package (starpunk/)
Purpose: Core application code organized as Python package.
Structure: Flat module layout (no sub-packages in V1)
Modules:
| Module | Purpose | Approximate LOC |
|---|---|---|
__init__.py |
Package init, create Flask app | 50 |
config.py |
Load configuration from .env | 75 |
database.py |
SQLite operations, schema management | 200 |
models.py |
Data models (Note, Session, Token) | 150 |
auth.py |
IndieAuth flow, session management | 200 |
micropub.py |
Micropub endpoint implementation | 250 |
feed.py |
RSS feed generation | 100 |
notes.py |
Note CRUD, file operations | 300 |
utils.py |
Slug generation, hashing, helpers | 100 |
Total Estimated: ~1,425 LOC for core application
Naming Convention:
- Lowercase with underscores
- Singular nouns for single-purpose modules (
config.py, notconfigs.py) - Plural for collections (
notes.pymanages many notes) - Descriptive names over abbreviations (
database.py, notdb.py)
Import Strategy:
# In app.py
from starpunk import create_app
app = create_app()
Rationale: Flat package structure is simpler than nested sub-packages. All modules are peers. No circular dependency issues.
Static Assets (static/)
Purpose: CSS, JavaScript, and other static files served directly.
Organization:
static/
├── css/
│ └── style.css # Single stylesheet
└── js/
└── preview.js # Optional markdown preview
CSS Structure (style.css):
- ~200 lines total
- CSS custom properties for theming
- Mobile-first responsive design
- Semantic HTML with minimal classes
JavaScript Structure (preview.js):
- Optional enhancement only
- Vanilla JavaScript (no frameworks)
- Markdown preview using marked.js from CDN
- Degrades gracefully if disabled
URL Pattern: /static/{type}/{file}
- Example:
/static/css/style.css - Example:
/static/js/preview.js
Future Assets (V2+):
- Favicon:
static/favicon.ico - Icons:
static/icons/ - Images:
static/images/(if needed)
Rationale: Standard Flask static file convention. Simple, flat structure since we have minimal assets.
Templates (templates/)
Purpose: Jinja2 HTML templates for server-side rendering.
Organization:
templates/
├── base.html # Public site base layout
├── index.html # Homepage (extends base.html)
├── note.html # Single note (extends base.html)
├── feed.xml # RSS feed (no layout)
└── admin/
├── base.html # Admin base layout
├── login.html # Login form
├── dashboard.html # Admin dashboard
├── new.html # Create note
└── edit.html # Edit note
Template Hierarchy:
base.html (public)
├── index.html (note list)
└── note.html (single note)
admin/base.html
├── admin/dashboard.html
├── admin/new.html
└── admin/edit.html
admin/login.html (no base, standalone)
feed.xml (no base, XML output)
Naming Convention:
- Lowercase with hyphens for multi-word names
- Descriptive names (
dashboard.html, notdash.html) - Base templates clearly named (
base.html)
Template Features:
- Microformats2 markup (h-entry, h-card)
- Semantic HTML5
- Accessible markup (ARIA labels)
- Mobile-responsive
- Progressive enhancement
Rationale: Standard Flask template convention. Admin templates in subdirectory keeps them organized.
Data Directory (data/)
Purpose: User-created content and database. This is the sacred directory.
Structure:
data/
├── notes/
│ └── {YYYY}/
│ └── {MM}/
│ └── {slug}.md
└── starpunk.db
Notes Directory (data/notes/):
- Pattern: Year/Month hierarchy (
YYYY/MM/) - Files: Markdown files with slug names
- Example:
data/notes/2024/11/my-first-note.md
Database File (data/starpunk.db):
- SQLite database
- Contains metadata, sessions, tokens
- NOT content (content is in .md files)
Permissions:
- Directory: 755 (rwxr-xr-x)
- Files: 644 (rw-r--r--)
- Database: 644 (rw-r--r--)
Backup Strategy:
# Simple backup
tar -czf starpunk-backup-$(date +%Y%m%d).tar.gz data/
# Or rsync
rsync -av data/ /backup/starpunk/
Gitignore: ENTIRE data/ directory must be gitignored.
Rationale: User data is completely isolated. Easy to backup. Portable across systems.
Tests (tests/)
Purpose: Complete test suite using pytest.
Organization:
tests/
├── __init__.py # Empty, marks as package
├── conftest.py # Pytest fixtures and configuration
├── test_auth.py # Authentication tests
├── test_database.py # Database operations tests
├── test_micropub.py # Micropub endpoint tests
├── test_feed.py # RSS feed generation tests
├── test_notes.py # Note management tests
└── test_utils.py # Utility function tests
Naming Convention:
- All test files:
test_{module}.py - All test functions:
def test_{function_name}(): - Fixtures in
conftest.py
Test Organization:
- One test file per application module
- Integration tests in same file as unit tests
- Use fixtures for common setup (database, app context)
Coverage Target: >80% for V1
Rationale: Standard pytest convention. Easy to run all tests or specific modules.
Documentation (docs/)
Purpose: Architecture, decisions, designs, and standards.
Organization: See Documentation Organization Standard
docs/
├── architecture/ # System-level architecture
├── decisions/ # ADRs (Architecture Decision Records)
├── design/ # Detailed technical designs
└── standards/ # Coding and process standards
Key Documents:
architecture/overview.md- System architecturearchitecture/technology-stack.md- Complete tech stackdecisions/ADR-*.md- All architectural decisionsdesign/project-structure.md- This documentstandards/python-coding-standards.md- Code style
Rationale: Clear separation of document types. Easy to find relevant documentation.
File Naming Conventions
Python Files
- Modules:
lowercase_with_underscores.py - Packages:
lowercase(no underscores if possible) - Classes:
PascalCase(in code, not filenames) - Functions:
lowercase_with_underscores(in code)
Examples:
starpunk/database.py # Good
starpunk/Database.py # Bad
starpunk/db.py # Bad (use full word)
starpunk/note_manager.py # Good if needed
Markdown Files (Notes)
- Pattern:
{slug}.md - Slug format:
lowercase-with-hyphens - Valid characters:
a-z,0-9,-(hyphen) - No spaces, no underscores, no special characters
Examples:
first-note.md # Good
my-thoughts-on-python.md # Good
2024-11-18-daily-note.md # Good (date prefix okay)
First Note.md # Bad (spaces)
first_note.md # Bad (underscores)
first-note.markdown # Bad (use .md)
Template Files
- Pattern:
lowercase.htmlorlowercase-name.html - XML:
.xmlextension for RSS feed
Examples:
base.html # Good
note.html # Good
admin/dashboard.html # Good
admin/new-note.html # Good (if multi-word)
admin/NewNote.html # Bad
Documentation Files
- Pattern:
lowercase-with-hyphens.md - ADRs:
ADR-{NNN}-{short-title}.md
Examples:
project-structure.md # Good
database-schema.md # Good
ADR-001-python-web-framework.md # Good
ProjectStructure.md # Bad
project_structure.md # Bad (use hyphens)
Configuration Files
- Pattern: Standard conventions (
.env,requirements.txt, etc.)
Examples:
.env # Good
.env.example # Good
requirements.txt # Good
requirements-dev.txt # Good
.gitignore # Good
Gitignore Requirements
The .gitignore file MUST include the following:
# Python
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
# Virtual Environment
.venv/
venv/
env/
ENV/
.venv.*
# Environment Configuration
.env
*.env
!.env.example
# User Data (CRITICAL - NEVER COMMIT)
data/
*.db
*.sqlite
*.sqlite3
# IDE
.vscode/
.idea/
*.swp
*.swo
*~
.DS_Store
# Testing
.pytest_cache/
.coverage
htmlcov/
*.cover
.hypothesis/
# Distribution
dist/
build/
*.egg-info/
*.egg
# Logs
*.log
logs/
Critical Rules:
- NEVER commit
data/directory - contains user data - NEVER commit
.envfile - contains secrets - NEVER commit
.venv/- virtual environment is local - ALWAYS commit
.env.example- template is safe
Directory Creation Order
When initializing project, create in this order:
-
Root files:
touch .gitignore touch .env.example touch README.md touch CLAUDE.MD touch LICENSE touch requirements.txt touch requirements-dev.txt -
Application package:
mkdir -p starpunk touch starpunk/__init__.py -
Static assets:
mkdir -p static/css static/js touch static/css/style.css touch static/js/preview.js -
Templates:
mkdir -p templates/admin touch templates/base.html touch templates/index.html touch templates/note.html touch templates/feed.xml touch templates/admin/base.html touch templates/admin/login.html touch templates/admin/dashboard.html touch templates/admin/new.html touch templates/admin/edit.html -
Data directory (created on first run):
mkdir -p data/notes -
Tests:
mkdir -p tests touch tests/__init__.py touch tests/conftest.py -
Documentation (mostly exists):
mkdir -p docs/architecture docs/decisions docs/design docs/standards
Path Standards
Absolute vs Relative Paths
In Code:
- Use relative paths from project root
- Let Flask handle path resolution
- Use
pathlib.Pathfor file operations
In Configuration:
- Support both absolute and relative paths
- Relative paths are relative to project root
- Document clearly in
.env.example
In Documentation:
- Use absolute paths for clarity
- Example:
/home/phil/Projects/starpunk/data/notes
In Agent Operations:
- ALWAYS use absolute paths
- Never rely on current working directory
- See ADR-006 for details
Path Construction Examples
from pathlib import Path
# Project root
PROJECT_ROOT = Path(__file__).parent.parent
# Data directory
DATA_DIR = PROJECT_ROOT / "data"
NOTES_DIR = DATA_DIR / "notes"
DB_PATH = DATA_DIR / "starpunk.db"
# Static files
STATIC_DIR = PROJECT_ROOT / "static"
CSS_DIR = STATIC_DIR / "css"
# Templates (handled by Flask)
TEMPLATE_DIR = PROJECT_ROOT / "templates"
File Size Guidelines
Target Sizes
| File Type | Target Size | Maximum Size | Rationale |
|---|---|---|---|
| Python module | 100-300 LOC | 500 LOC | Keep modules focused |
| Template | 50-100 lines | 200 lines | Use template inheritance |
| CSS | 200 LOC total | 300 LOC | Minimal styling only |
| JavaScript | 100 LOC | 200 LOC | Optional feature only |
| Markdown note | 50-500 words | No limit | User content |
| Test file | 100-200 LOC | 400 LOC | One module per file |
If file exceeds maximum: Consider splitting into multiple modules or refactoring.
Module Organization
starpunk/init.py
Purpose: Package initialization and Flask app factory.
Contents:
create_app()function- Blueprint registration
- Configuration loading
Example:
from flask import Flask
def create_app():
app = Flask(__name__)
# Load configuration
from starpunk.config import load_config
load_config(app)
# Initialize database
from starpunk.database import init_db
init_db(app)
# Register blueprints
from starpunk.routes import public, admin, api
app.register_blueprint(public.bp)
app.register_blueprint(admin.bp)
app.register_blueprint(api.bp)
return app
app.py (Root)
Purpose: Application entry point for Flask.
Contents:
from starpunk import create_app
app = create_app()
if __name__ == '__main__':
app.run(debug=True)
Rationale: Minimal entry point. All logic in package.
Import Organization
Follow PEP 8 import ordering:
- Standard library imports
- Third-party imports
- Local application imports
Example:
# Standard library
import os
import sqlite3
from pathlib import Path
from datetime import datetime
# Third-party
from flask import Flask, render_template
import markdown
import httpx
# Local
from starpunk.config import load_config
from starpunk.database import get_db
from starpunk.models import Note
Future Structure Extensions (V2+)
Potential Additions
Media Uploads:
data/
├── notes/
├── media/
│ └── {YYYY}/{MM}/
│ └── {filename}
└── starpunk.db
Migrations:
migrations/
├── 001_initial_schema.sql
├── 002_add_media_table.sql
└── 003_add_tags.sql
Deployment:
deploy/
├── systemd/
│ └── starpunk.service
├── nginx/
│ └── starpunk.conf
└── docker/
└── Dockerfile
Do NOT create these in V1 - only when needed.
Verification Checklist
After creating project structure, verify:
- All directories exist
.gitignoreis configured correctly.env.exampleexists (.envdoes not)data/directory is gitignored- All
__init__.pyfiles exist in Python packages - Template hierarchy is correct
- Static files are in correct locations
- Tests directory mirrors application structure
- Documentation is organized correctly
- No placeholder files committed (except templates)
Anti-Patterns to Avoid
Don't:
- Create deeply nested directories (max 3 levels)
- Use abbreviations in directory names (
tpl/instead oftemplates/) - Mix code and data (keep
data/separate) - Put configuration in code (use
.env) - Create empty placeholder directories
- Use both
src/and package name (pick one - we chose package) - Create
scripts/,bin/,tools/directories in V1 (YAGNI) - Put tests inside application package (keep separate)
Do:
- Keep structure flat where possible
- Use standard conventions (Flask, Python, pytest)
- Separate concerns (code, data, tests, docs, static)
- Make structure obvious to newcomers
- Follow principle: "Every directory must justify its existence"
Summary
Total Directories: 12 top-level directories/files Total Python Modules: ~9 in starpunk package Total Templates: 9 HTML/XML files Total LOC Estimate: ~1,500 LOC application + 500 LOC tests = 2,000 total
Philosophy: Simple, flat, conventional structure. Every file and directory has a clear purpose. User data is isolated and portable. Documentation is comprehensive and organized.