diff --git a/.runner b/.runner deleted file mode 100644 index f7d4203..0000000 --- a/.runner +++ /dev/null @@ -1,11 +0,0 @@ -{ - "WARNING": "This file is automatically generated by forgejo-runner. Do not edit it manually unless you know what you are doing. Removing this file will cause act runner to re-register as a new runner.", - "id": 14, - "uuid": "2f3252bd-5f69-4d3c-8f77-8b54caf784af", - "name": "nixos-runner", - "token": "993ebcbb295c7c58ac35fd5c2ec07cc752c99152", - "address": "https://git.derickphan.com", - "labels": [ - "ubuntu-latest:docker" - ] -} diff --git a/README.md b/README.md index 71015c0..e44c450 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,6 @@ A personal, decentralized search engine built on the [Reticulum](https://reticul - **Custom templates** — Full HTML/CSS/JS template editor to personalize your instance - **Import/export** — JSON-based backup and restore - **Mesh-native** — Works over Reticulum without the internet; encrypted and decentralized by default -- **Forum plugin** — Optional link-sharing discussion board over the mesh (see Forum section below) ## Performance & Scale @@ -51,37 +50,34 @@ Run the downloaded file — no installation required. ## Docker -TinyWeb is distributed as source. Clone the repo, then build and run with Docker Compose: +Pull and run TinyWeb from the container registry: ```bash -git clone https://git.derickphan.com/lichenblankie/tinyweb.git -cd tinyweb -docker compose up -d +docker run -p 8080:8080 registry.derickphan.com/tinyweb:latest ``` -The bundled `docker-compose.yml` builds the image from source and persists your data in a named volume: +Or with a specific version: + +```bash +docker run -p 8080:8080 registry.derickphan.com/tinyweb:v0.1.0 +``` + +### Docker Compose ```yaml services: tinyweb: - build: . + image: registry.derickphan.com/tinyweb:latest ports: - "8080:8080" volumes: - tinyweb-data:/data - restart: unless-stopped volumes: tinyweb-data: ``` -After the first build, the image is cached locally and subsequent `docker compose up -d` calls are instant. To update to the latest source: - -```bash -git pull && docker compose up -d --build -``` - -If you're on macOS or need to reach a Reticulum node over TCP, uncomment the `RNS_TCP_HOST` / `RNS_TCP_PORT` block in `docker-compose.yml` and point it at a host running Reticulum. On Linux with LAN auto-discovery, leave it as-is (or switch to `network_mode: host`). +Run with `docker compose up -d`. ### Storage Estimates @@ -108,7 +104,6 @@ Your data is stored in `~/.tinyweb/`: |------|-------------| | `index.db` | SQLite database with your indexed pages | | `tinyweb_identity` | Your Reticulum identity (keep safe!) | -| `forum.db` | Forum plugin database (only if forum is enabled) | | `models/` | Downloaded AI models for semantic search | | `index.hnsw` | Semantic search index | @@ -120,7 +115,6 @@ Back up the whole `~/.tinyweb/` directory periodically. The two files that matte - **`tinyweb_identity`** is your permanent mesh identity. If you lose it, your destination hash changes and every subscriber has to re-subscribe to the new one. Keep it somewhere you trust; the file is `0600` by default. - **`index.db`** is your full reading history — every page, note, tag, and synced remote page. Losing it loses everything you've curated. -- **`forum.db`** (if the forum plugin is enabled) — all threads, posts, upvotes, and moderation settings. Losing it loses your forum data. `models/` and `index.hnsw` are re-derivable (the model will re-download, and the HNSW index rebuilds from the database on next startup with semantic search enabled) so they don't need to be backed up. @@ -128,18 +122,13 @@ The `/export` page produces a JSON dump of your pages. It's a migration aid — ### Docker -When you run via `docker compose up` (above), data is stored in the `tinyweb-data` named volume and persists across rebuilds. To inspect or back up: +Data is stored in the `/data` volume inside the container. Use a volume mount to persist data: ```bash -docker compose exec tinyweb ls -la /data -docker compose down # stop without removing the volume +docker run -p 8080:8080 -v tinyweb-data:/data registry.derickphan.com/tinyweb:latest ``` -To reset everything (destroys your index and identity — back up first): - -```bash -docker compose down -v -``` +Or with docker-compose (see above) — data persists in the named volume. ### Command line options @@ -179,31 +168,6 @@ This connects over Reticulum and serves the remote instance at `http://localhost 3. **Subscribe** — Add a friend's destination hash on `/subscriptions` to sync their shared index 4. **Customize** — Edit your site name, HTML template, and sharing settings on `/style` -## Forum plugin - -TinyWeb ships with an optional [tinyweb-forum](https://git.derickphan.com/lichenblankie/tinyweb-forum) plugin — a decentralized link-sharing discussion board that runs in-process alongside TinyWeb. - -### Install - -```bash -pip install tinyweb-forum -``` - -Enable it on the `/style` page under "Forum". A "Forum" link will appear in the navigation bar. - -### How it works - -- Threads and posts are stored in `~/.tinyweb/forum.db` (separate from your search index) -- Instances are discovered automatically via mesh announces — no manual setup needed -- Sync is manual by default: click "sync now" on the forum page. Auto-sync every 5 minutes is optional (toggle on moderation page) -- At scale, sync uses epidemic gossip: 20 random peers per cycle, converging globally within ~O(log N) cycles -- Authors are identified by a short pseudonymous identity hash (no accounts, no sign-up) -- Auto-discovery can be disabled in the moderation page -- Threads are auto-pruned after 30 days (configurable, or set to 0 to keep everything) -- Moderation is local: block authors, mute threads, keyword filters, and gossip block lists with peers (auto-block after 3 peer reports) - -For full feature docs, see the [tinyweb-forum README](https://git.derickphan.com/lichenblankie/tinyweb-forum). - ## Project structure ``` @@ -229,7 +193,6 @@ Other hardening measures: - **XSS escaping** — All user-supplied content is HTML-escaped before rendering - **Bookmark authentication** — The bookmarklet endpoint requires a secret token - **Identity file protection** — The Reticulum identity key is restricted to owner-only permissions (0600) -- **Forum caveats** — See [tinyweb-forum Security](https://git.derickphan.com/lichenblankie/tinyweb-forum#security) for forum-specific risks (voluntary retractions, block gossip manipulation, no rate limiting) ## Maintenance diff --git a/app.py b/app.py index 3830c85..b1c4fe6 100644 --- a/app.py +++ b/app.py @@ -8,8 +8,6 @@ from http.server import HTTPServer from db import init_db, get_setting, set_setting from handlers import dispatch_request -import handlers as handlers_mod -import templates as templates_mod import gateway from gateway import GatewayState, GatewayHandler @@ -33,7 +31,6 @@ def find_available_port(start=8080, max_attempts=20, host="127.0.0.1"): for port in range(start, start + max_attempts): try: with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: - s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) s.bind((host, port)) return port except OSError: @@ -100,7 +97,6 @@ def rns_request_handler(path, data, request_id, link_id, remote_identity, reques def start_gateway(reticulum, bind_host="127.0.0.1"): GatewayState.reticulum = reticulum GatewayState.local_dispatch = dispatch_request - HTTPServer.allow_reuse_address = True server = HTTPServer((bind_host, gateway.GATEWAY_PORT), GatewayHandler) thread = threading.Thread(target=server.serve_forever, daemon=True) thread.start() @@ -275,22 +271,6 @@ def main(): allow=RNS.Destination.ALLOW_ALL, ) - # Initialize forum plugin if available - forum = None - try: - from tinyweb_forum import ForumPlugin - from db import get_site_name - forum = ForumPlugin(DATA_DIR, identity, reticulum, site_name=get_site_name()) - if get_setting("forum_enabled", "0") == "1": - forum.enable() - templates_mod.FORUM_ENABLED = True - handlers_mod.forum_plugin = forum - print(f"Forum plugin: {'enabled' if forum.is_enabled() else 'available (enable in settings)'}") - except ImportError: - print("Forum plugin not installed (pip install tinyweb[forum])") - except Exception as e: - print(f"Forum plugin error: {e}") - # Brief delay to ensure all interfaces (especially TCP) are fully ready time.sleep(2) destination.announce() diff --git a/db.py b/db.py index ec13254..8a645ab 100644 --- a/db.py +++ b/db.py @@ -291,24 +291,6 @@ def init_db(): db.execute("CREATE INDEX IF NOT EXISTS idx_page_tags_page ON page_tags(page_id)") db.execute("CREATE INDEX IF NOT EXISTS idx_page_tags_tag ON page_tags(tag_id)") - # Migrate custom_template: replace hardcoded forum link with {{forum_link}} placeholder - cur = db.execute("SELECT value FROM settings WHERE key='custom_template'") - row = cur.fetchone() - if row: - updated = row[0].replace('forum', "{{forum_link}}") - if updated != row[0]: - db.execute("UPDATE settings SET value=? WHERE key='custom_template'", (updated,)) - db.commit() - - # Migrate custom_template: replace hardcoded site name with {{site_name}} placeholder - cur = db.execute("SELECT value FROM settings WHERE key='custom_template'") - row = cur.fetchone() - if row and '{{site_name}}' not in row[0]: - updated = row[0].replace('href="/">tinyweb', 'href="/">{{site_name}}') - if updated != row[0]: - db.execute("UPDATE settings SET value=? WHERE key='custom_template'", (updated,)) - db.commit() - db.execute("PRAGMA journal_mode=WAL") db.execute("PRAGMA synchronous=NORMAL") db.execute("PRAGMA cache_size=-64000") diff --git a/handlers.py b/handlers.py index 523000f..e47520b 100644 --- a/handlers.py +++ b/handlers.py @@ -6,11 +6,9 @@ from datetime import datetime from urllib.parse import unquote from db import get_db, return_db, get_setting, set_setting, get_site_name, index_url, clean_url -import templates as templates_mod from templates import esc, wrap_page, DEFAULT_TEMPLATE from rns_client import fetch_remote_sites -forum_plugin = None _request_local = threading.local() @@ -362,7 +360,7 @@ def handle_search(query): ) -def handle_add_form(msg="", action_type="index", prefill_url=""): +def handle_add_form(msg="", action_type="index"): if action_type == "subscribe": return _respond( f"
{msg}
" f'back' ) - url_value = f'value="{esc(prefill_url)}" ' if prefill_url else "" return _respond( f"Add a site to your index
" f'