English | Русский
PicoBlog is a lightweight and performant blog platform, designed as an alternative to WriteFreely, with a focus on simplicity and efficiency, especially for deployment on devices like Raspberry Pi.
- User Management: Admin-only user registration and role assignment.
- Access Control: Administrators can assign tags to users to control which blog posts they can view.
- Tag Management: Administrators can create, edit, and delete tags.
- Post Management: Administrators can create, edit, and delete blog posts with Markdown support.
- Markdown Rendering: Blog post content is written in Markdown and rendered to HTML for display.
- Minimalist Design: Built with a focus on a clean and simple user interface, styled using Tailwind CSS (compiled, no CDN).
- Copy Button: One-click copying for code blocks in posts.
- RSS Feed: Standard RSS 2.0 feed for public posts.
- Dark Mode: JavaScript-free dark mode support with cookie persistence.
- CI/CD: Automated testing and deployment pipelines.
PicoBlog implements a refined tag-based access control system:
-
Public vs. Private Access: Controlled by the
REQUIRE_LOGINenvironment variable.REQUIRE_LOGIN=True(default): Only logged-in (authenticated) users can view any posts.REQUIRE_LOGIN=False: Blog posts without any tags are visible to everyone (including anonymous guests).
-
Protected Posts (Tags Required): Any post with at least one tag always requires authentication and specific permissions.
-
Posts with Regular Tags (OR Logic):
- If a post has only regular tags (i.e., no master tags), an authenticated user can view it if they have been assigned at least one of the tags that are applied to that post.
- This works as an "OR" condition: possessing
Tag AORTag Bgrants access if the post hasTag AandTag B.
-
Posts with Master Tags (AND Logic):
- Some tags can be designated as "Master Tags" (configured with
is_master=Truein the admin panel). These tags enforce stricter access. - If a post has any Master Tags, an authenticated user must possess all of the Master Tags applied to that post to gain access.
- This works as an "AND" condition: possessing
Master Tag XANDMaster Tag Ygrants access if the post hasMaster Tag XandMaster Tag Y.
- Some tags can be designated as "Master Tags" (configured with
-
Posts with Mixed Tags (Master AND Regular OR Logic):
- If a post has both Master Tags and regular tags, an authenticated user must satisfy both conditions:
- Possess all of the Master Tags applied to the post (AND logic).
- Possess at least one of the regular tags applied to the post (OR logic).
- Failure to meet either condition will deny access.
- If a post has both Master Tags and regular tags, an authenticated user must satisfy both conditions:
This system ensures granular control over content visibility, allowing for both broad access to general topics and restricted access to sensitive material.
The easiest way to run PicoBlog is using Docker.
-
Configure Environment:
cp .env.example .env # Generate secret key sed -i "s/^SECRET_KEY=.*/SECRET_KEY='$(python3 -c 'import secrets; print(secrets.token_hex(32))')'/" .env
-
Run:
docker-compose up -d --build
The blog will be available at
http://localhost:8000.
Follow these instructions to set up and run PicoBlog on a Linux system.
- Python 3.10+: Ensure you have Python 3.10 or a newer version installed.
- uv: A fast Python package installer and resolver.
pip install uv
- Node.js 16+ & npm: Required for building Tailwind CSS.
# Debian/Ubuntu sudo apt install nodejs npm
git clone https://github.com/Lowara1243/PicoBlog.git
cd PicoBlogIt's highly recommended to use a virtual environment to manage project dependencies.
uv venv
source .venv/bin/activate
uv syncTip
Termux (Android) users: To install image processing dependencies on ARM, run:
pkg install python-pillow
or ensure you have system libraries installed: pkg install libjpeg-turbo libpng libwebp
PicoBlog uses Tailwind CSS which needs to be compiled.
npm install
npm run build:cssFor development with automatic CSS rebuilding:
npm run watch:cssPicoBlog is configured using environment variables. You can set these in a .env file in the project root.
| Variable | Description | Default |
|---|---|---|
SECRET_KEY |
Required. A long random string used for session security. | None (Must be set in production) |
FLASK_ENV |
Set to production for production environments. |
development |
DATABASE_URL |
Database connection URL. | sqlite:////path/to/app.db |
REQUIRE_LOGIN |
True to require login for all posts (private mode). False allows public viewing of untagged posts. |
True |
Create a .env file in the project root directory based on .env.example and fill in your secret key and database URL.
cp .env.example .env
# Open .env with your editor and modify if necessaryApply the database migrations to set up the database schema.
export FLASK_APP=app
flask db upgradeAn admin user is required to access the administration panel and manage users, tags, and posts. You can create one using the Flask shell.
export FLASK_APP=app
flask shellInside the Flask shell, run the following commands:
from app import db
from app.models import User
admin_user = User.query.filter_by(username='admin').first()
if admin_user is None:
admin_user = User(username='admin', email='admin@example.com', is_admin=True)
admin_user.set_password(r'adminpassword') # IMPORTANT: Change this to a strong, unique password!
db.session.add(admin_user)
db.session.commit()
print('Admin user created.')
else:
print('Admin user already exists.')
exit()export FLASK_APP=app
flask runThe application will typically run on http://127.0.0.1:5000/.
- Navigate to
http://127.0.0.1:5000/loginand log in with the admin credentials you created. - Once logged in, you will see an "Admin" link in the navigation bar. Click on it to access the admin dashboard.
- From the admin dashboard, you can manage users, tags, and posts.
Some critical administrative tasks can only be performed via the Flask shell for security reasons. Below are common scenarios.
If a user forgot their password or you need to reset it:
export FLASK_APP=app
flask shellfrom app import db
from app.models import User
# Find the user by username
user = User.query.filter_by(username='username_here').first()
if user:
user.set_password(r'new_secure_password')
db.session.commit()
print(f'Password for {user.username} has been reset.')
else:
print('User not found.')
exit()To transfer admin rights to another user or replace the current administrator:
export FLASK_APP=app
flask shellfrom app import db
from app.models import User
# Remove admin rights from current admin
old_admin = User.query.filter_by(username='old_admin_username').first()
if old_admin:
old_admin.is_admin = False
print(f'Admin rights removed from {old_admin.username}')
# Grant admin rights to new user
new_admin = User.query.filter_by(username='new_admin_username').first()
if new_admin:
new_admin.is_admin = True
print(f'Admin rights granted to {new_admin.username}')
db.session.commit()
exit()Note: Admin status cannot be changed through the web interface. Only one administrator is recommended for security and simplicity.
WARNING: This will delete ALL data including users, posts, tags, and comments. Use with extreme caution!
# Stop the application if it's running
# Remove the database file
rm data/app.db
# Remove uploaded files (optional)
rm -rf app/static/uploads/*
# Recreate the database schema
export FLASK_APP=app
flask db upgrade
# Create a new admin user (see step 6 in setup instructions)
flask shellfrom app import db
from app.models import User
admin = User(username='admin', email='admin@example.com', is_admin=True)
admin.set_password(r'your_secure_password')
db.session.add(admin)
db.session.commit()
print('Database reset complete. New admin user created.')
exit()After resetting, restart the application.
For detailed deployment instructions (Nginx, Gunicorn, systemd), see docs/EN/DEPLOYMENT.md.
For more technical details, check out: