From f27321888d03d67768c6c2a1476614d9fd8364f8 Mon Sep 17 00:00:00 2001 From: blankie Date: Wed, 17 Jun 2026 19:10:33 +0000 Subject: [PATCH 1/8] subscriptions: add forum_enabled toggle for trust circle --- src/tinyweb/db.py | 6 ++++++ src/tinyweb/handlers/__init__.py | 4 ++++ src/tinyweb/handlers/subscriptions.py | 13 +++++++++++++ 3 files changed, 23 insertions(+) diff --git a/src/tinyweb/db.py b/src/tinyweb/db.py index d7f269b..0e5cea2 100644 --- a/src/tinyweb/db.py +++ b/src/tinyweb/db.py @@ -270,6 +270,12 @@ def init_db(): db.execute("ALTER TABLE pages ADD COLUMN reticulum_dest TEXT DEFAULT ''") db.commit() + # Migrate subscriptions: add forum_enabled column + sub_cols = [row[1] for row in db.execute("PRAGMA table_info(subscriptions)").fetchall()] + if "forum_enabled" not in sub_cols: + db.execute("ALTER TABLE subscriptions ADD COLUMN forum_enabled INTEGER DEFAULT 0") + db.commit() + # Chunks table for semantic search embeddings db.execute( "CREATE TABLE IF NOT EXISTS chunks (" diff --git a/src/tinyweb/handlers/__init__.py b/src/tinyweb/handlers/__init__.py index 524ae8d..a09b073 100644 --- a/src/tinyweb/handlers/__init__.py +++ b/src/tinyweb/handlers/__init__.py @@ -29,6 +29,7 @@ from .subscriptions import ( handle_subscriptions, handle_subscription_add, handle_subscription_browse, handle_subscription_pick, _sync_subscription, handle_subscription_sync, handle_subscription_autosync, + handle_subscription_forum, handle_subscription_delete, handle_subscription_syncall, _sync_threads, ) @@ -150,6 +151,9 @@ def _dispatch_inner(data): elif path.startswith("/subscriptions/autosync/"): sid = extract_id("/subscriptions/autosync/") return handle_subscription_autosync(sid) if sid is not None else _error(400) + elif path.startswith("/subscriptions/forum/"): + sid = extract_id("/subscriptions/forum/") + return handle_subscription_forum(sid) if sid is not None else _error(400) elif path.startswith("/subscriptions/delete/"): sid = extract_id("/subscriptions/delete/") return handle_subscription_delete(sid) if sid is not None else _error(400) diff --git a/src/tinyweb/handlers/subscriptions.py b/src/tinyweb/handlers/subscriptions.py index 0113d07..1e0b5b7 100644 --- a/src/tinyweb/handlers/subscriptions.py +++ b/src/tinyweb/handlers/subscriptions.py @@ -146,6 +146,7 @@ def handle_subscriptions(msg=""): for s in subs: sub_id = s["id"] auto_label = "on" if s["auto_sync"] else "off" + forum_label = "on" if s.get("forum_enabled") else "off" last = s["last_sync"] or "never" sync_status = get_setting(f"sync_status_{sub_id}", "") is_syncing = sub_id in _sync_threads and _sync_threads[sub_id].is_alive() @@ -177,6 +178,8 @@ def handle_subscriptions(msg=""): f'{sync_btn}' f'
' f'{_csrf_field()}
' + f'
' + f'{_csrf_field()}
' f'
' f'{_csrf_field()}
' f'' @@ -430,6 +433,16 @@ def handle_subscription_autosync(sub_id): return _redirect("/subscriptions") +def handle_subscription_forum(sub_id): + db = get_db() + try: + db.execute("UPDATE subscriptions SET forum_enabled = 1 - forum_enabled WHERE id = ?", (sub_id,)) + db.commit() + finally: + return_db(db) + return _redirect("/subscriptions") + + def handle_subscription_delete(sub_id): db = get_db() try: From 25af71b591f7e3ffe4213e4cab08d634211e8cd2 Mon Sep 17 00:00:00 2001 From: blankie Date: Wed, 17 Jun 2026 19:11:25 +0000 Subject: [PATCH 2/8] readme: document forum trust circle --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index b38ae0e..ac2206b 100644 --- a/README.md +++ b/README.md @@ -185,6 +185,7 @@ This connects over Reticulum and serves the remote instance at `http://localhost - Desktop-oriented - JSON-only import - Forum threads prune after 30 days by default +- Forum trust circle replaces open discovery — you discover people through your existing subscription graph, not topic blooms - Best-effort maintenance ## Forum plugin @@ -209,6 +210,7 @@ Enable it on the `/style` page under "Forum". A "Forum" link will appear in the - 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) +- **Trust circle** — Content is gated by a trust graph derived from your TinyWeb subscriptions. Each subscription has a "forum" toggle — enable it to include that peer's forum content (and their transitive trust network). Blocking a peer cascades: their downstream trust network is removed from your view. This makes spam structurally impossible — new identities have zero reach without a human vouching for them. For full feature docs, see the [tinyweb-forum README](https://codeberg.org/tinyweb/tinyweb-forum). From 3f077eff81348698b10a83b8209c2e9a489da01a Mon Sep 17 00:00:00 2001 From: blankie Date: Wed, 17 Jun 2026 19:12:49 +0000 Subject: [PATCH 3/8] readme: tone down trust circle language --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ac2206b..9b32aa1 100644 --- a/README.md +++ b/README.md @@ -185,7 +185,7 @@ This connects over Reticulum and serves the remote instance at `http://localhost - Desktop-oriented - JSON-only import - Forum threads prune after 30 days by default -- Forum trust circle replaces open discovery — you discover people through your existing subscription graph, not topic blooms +- Forum discovery is through the subscription graph rather than topic-based blooms - Best-effort maintenance ## Forum plugin @@ -210,7 +210,7 @@ Enable it on the `/style` page under "Forum". A "Forum" link will appear in the - 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) -- **Trust circle** — Content is gated by a trust graph derived from your TinyWeb subscriptions. Each subscription has a "forum" toggle — enable it to include that peer's forum content (and their transitive trust network). Blocking a peer cascades: their downstream trust network is removed from your view. This makes spam structurally impossible — new identities have zero reach without a human vouching for them. +- **Trust circle** — Content is gated by a trust graph derived from your TinyWeb subscriptions. Each subscription has a "forum" toggle — enable it to include that peer's forum content (and their transitive trust network). Blocking a peer cascades: their downstream trust network is removed from your view. New identities have no trust path until a trusted peer vouches for them. For full feature docs, see the [tinyweb-forum README](https://codeberg.org/tinyweb/tinyweb-forum). From 893c9cbb158da3d963801cab29f158c6641c5243 Mon Sep 17 00:00:00 2001 From: blankie Date: Wed, 17 Jun 2026 19:15:39 +0000 Subject: [PATCH 4/8] app: fix gateway import alias (broken since src layout migration) --- src/tinyweb/app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tinyweb/app.py b/src/tinyweb/app.py index ca3b6de..b63ed3c 100644 --- a/src/tinyweb/app.py +++ b/src/tinyweb/app.py @@ -10,7 +10,7 @@ from tinyweb.db import init_db, get_setting, set_setting from tinyweb.handlers import dispatch_request import tinyweb.handlers as handlers_mod import tinyweb.templates as templates_mod -import tinyweb.gateway +import tinyweb.gateway as gateway from tinyweb.gateway import GatewayState, GatewayHandler IDENTITY_FILE = "tinyweb_identity" From b3426b24946ddb6638104169a94808cc68854422 Mon Sep 17 00:00:00 2001 From: blankie Date: Wed, 17 Jun 2026 19:16:53 +0000 Subject: [PATCH 5/8] subscriptions: fix sqlite3.Row access (no .get() method) --- src/tinyweb/handlers/subscriptions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tinyweb/handlers/subscriptions.py b/src/tinyweb/handlers/subscriptions.py index 1e0b5b7..8732de1 100644 --- a/src/tinyweb/handlers/subscriptions.py +++ b/src/tinyweb/handlers/subscriptions.py @@ -146,7 +146,7 @@ def handle_subscriptions(msg=""): for s in subs: sub_id = s["id"] auto_label = "on" if s["auto_sync"] else "off" - forum_label = "on" if s.get("forum_enabled") else "off" + forum_label = "on" if s["forum_enabled"] else "off" last = s["last_sync"] or "never" sync_status = get_setting(f"sync_status_{sub_id}", "") is_syncing = sub_id in _sync_threads and _sync_threads[sub_id].is_alive() From 3f00554f0934e3c2aa8cc7812d91c7e8114a0d1a Mon Sep 17 00:00:00 2001 From: blankie Date: Wed, 17 Jun 2026 19:51:21 +0000 Subject: [PATCH 6/8] remove forum_enabled toggle: subscribing IS trusting, no intermediate state --- README.md | 2 +- src/tinyweb/handlers/__init__.py | 4 ---- src/tinyweb/handlers/subscriptions.py | 13 ------------- 3 files changed, 1 insertion(+), 18 deletions(-) diff --git a/README.md b/README.md index 9b32aa1..c52f06e 100644 --- a/README.md +++ b/README.md @@ -210,7 +210,7 @@ Enable it on the `/style` page under "Forum". A "Forum" link will appear in the - 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) -- **Trust circle** — Content is gated by a trust graph derived from your TinyWeb subscriptions. Each subscription has a "forum" toggle — enable it to include that peer's forum content (and their transitive trust network). Blocking a peer cascades: their downstream trust network is removed from your view. New identities have no trust path until a trusted peer vouches for them. +- **Trust circle** — Content is gated by a trust graph derived from your TinyWeb subscriptions. Each subscribed peer's forum content (and their transitive trust network) enters your view automatically. Blocking a peer cascades: their downstream trust network is removed from your view. For full feature docs, see the [tinyweb-forum README](https://codeberg.org/tinyweb/tinyweb-forum). diff --git a/src/tinyweb/handlers/__init__.py b/src/tinyweb/handlers/__init__.py index a09b073..524ae8d 100644 --- a/src/tinyweb/handlers/__init__.py +++ b/src/tinyweb/handlers/__init__.py @@ -29,7 +29,6 @@ from .subscriptions import ( handle_subscriptions, handle_subscription_add, handle_subscription_browse, handle_subscription_pick, _sync_subscription, handle_subscription_sync, handle_subscription_autosync, - handle_subscription_forum, handle_subscription_delete, handle_subscription_syncall, _sync_threads, ) @@ -151,9 +150,6 @@ def _dispatch_inner(data): elif path.startswith("/subscriptions/autosync/"): sid = extract_id("/subscriptions/autosync/") return handle_subscription_autosync(sid) if sid is not None else _error(400) - elif path.startswith("/subscriptions/forum/"): - sid = extract_id("/subscriptions/forum/") - return handle_subscription_forum(sid) if sid is not None else _error(400) elif path.startswith("/subscriptions/delete/"): sid = extract_id("/subscriptions/delete/") return handle_subscription_delete(sid) if sid is not None else _error(400) diff --git a/src/tinyweb/handlers/subscriptions.py b/src/tinyweb/handlers/subscriptions.py index 8732de1..0113d07 100644 --- a/src/tinyweb/handlers/subscriptions.py +++ b/src/tinyweb/handlers/subscriptions.py @@ -146,7 +146,6 @@ def handle_subscriptions(msg=""): for s in subs: sub_id = s["id"] auto_label = "on" if s["auto_sync"] else "off" - forum_label = "on" if s["forum_enabled"] else "off" last = s["last_sync"] or "never" sync_status = get_setting(f"sync_status_{sub_id}", "") is_syncing = sub_id in _sync_threads and _sync_threads[sub_id].is_alive() @@ -178,8 +177,6 @@ def handle_subscriptions(msg=""): f'{sync_btn}' f'
' f'{_csrf_field()}
' - f'
' - f'{_csrf_field()}
' f'
' f'{_csrf_field()}
' f'' @@ -433,16 +430,6 @@ def handle_subscription_autosync(sub_id): return _redirect("/subscriptions") -def handle_subscription_forum(sub_id): - db = get_db() - try: - db.execute("UPDATE subscriptions SET forum_enabled = 1 - forum_enabled WHERE id = ?", (sub_id,)) - db.commit() - finally: - return_db(db) - return _redirect("/subscriptions") - - def handle_subscription_delete(sub_id): db = get_db() try: From f418ce7c7540367e10d35e6abaf66a96e8946bd6 Mon Sep 17 00:00:00 2001 From: blankie Date: Wed, 17 Jun 2026 20:15:50 +0000 Subject: [PATCH 7/8] forum trust circle: trust-gated content exchange via subscription graph --- README.md | 2 ++ src/tinyweb/db.py | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/README.md b/README.md index b38ae0e..c52f06e 100644 --- a/README.md +++ b/README.md @@ -185,6 +185,7 @@ This connects over Reticulum and serves the remote instance at `http://localhost - Desktop-oriented - JSON-only import - Forum threads prune after 30 days by default +- Forum discovery is through the subscription graph rather than topic-based blooms - Best-effort maintenance ## Forum plugin @@ -209,6 +210,7 @@ Enable it on the `/style` page under "Forum". A "Forum" link will appear in the - 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) +- **Trust circle** — Content is gated by a trust graph derived from your TinyWeb subscriptions. Each subscribed peer's forum content (and their transitive trust network) enters your view automatically. Blocking a peer cascades: their downstream trust network is removed from your view. For full feature docs, see the [tinyweb-forum README](https://codeberg.org/tinyweb/tinyweb-forum). diff --git a/src/tinyweb/db.py b/src/tinyweb/db.py index d7f269b..0e5cea2 100644 --- a/src/tinyweb/db.py +++ b/src/tinyweb/db.py @@ -270,6 +270,12 @@ def init_db(): db.execute("ALTER TABLE pages ADD COLUMN reticulum_dest TEXT DEFAULT ''") db.commit() + # Migrate subscriptions: add forum_enabled column + sub_cols = [row[1] for row in db.execute("PRAGMA table_info(subscriptions)").fetchall()] + if "forum_enabled" not in sub_cols: + db.execute("ALTER TABLE subscriptions ADD COLUMN forum_enabled INTEGER DEFAULT 0") + db.commit() + # Chunks table for semantic search embeddings db.execute( "CREATE TABLE IF NOT EXISTS chunks (" From 31c53b9a8ef7cca0885ec170974f4897c6a4bb4b Mon Sep 17 00:00:00 2001 From: blankie Date: Wed, 17 Jun 2026 20:16:06 +0000 Subject: [PATCH 8/8] fix: app.py gateway import alias (broken since src layout migration) --- src/tinyweb/app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tinyweb/app.py b/src/tinyweb/app.py index ca3b6de..b63ed3c 100644 --- a/src/tinyweb/app.py +++ b/src/tinyweb/app.py @@ -10,7 +10,7 @@ from tinyweb.db import init_db, get_setting, set_setting from tinyweb.handlers import dispatch_request import tinyweb.handlers as handlers_mod import tinyweb.templates as templates_mod -import tinyweb.gateway +import tinyweb.gateway as gateway from tinyweb.gateway import GatewayState, GatewayHandler IDENTITY_FILE = "tinyweb_identity"