diff options
| author | Christophe Besson <cbesson@gmail.com> | 2026-08-16 15:28:28 +0200 |
|---|---|---|
| committer | Christophe Besson <cbesson@gmail.com> | 2026-08-16 15:28:28 +0200 |
| commit | 0bd3f805ffbd04b40b5150474336a5e5d200e72b (patch) | |
| tree | 2a92a21e02bae823e9ed52d489d74ba8db3de9b6 /packages/meshbay-hub/src/meshbay_hub/db | |
| parent | 0231d240b92a2a11042fa62c0222d4c4b96a859d (diff) | |
| download | meshbay-0bd3f805ffbd04b40b5150474336a5e5d200e72b.tar.gz | |
feat(hub): leaving a group, a cap on public ones, and hosting as a precondition
Leaving is its own endpoint rather than a relaxation of the owner's removal
check — an authorization rule with an exception in it is the one that gets read
wrong later. The owner cannot leave: the group would be left with nobody able
to admit, edit or delete it, which is the answer removal and account deletion
already give.
Public groups are capped at ten live ones per owner. They are the ones that
cost other people something — listed in Discover, joinable by anyone — so a
script that opens hundreds fills the directory for everybody. Private groups
are invisible to non-members and are not capped. Hub staff are exempt; the cap
is anti-spam, not a rule about running an instance. Creation is the only place
it can be checked, and deliberately so, because PATCH refuses to change
visibility at all.
A group is now listed only once a node has announced that it hosts it. Before
that it has no files, no key and nothing to connect to, so showing it to a
member produces a name they cannot open and cannot be told why; its owner still
sees it while they set the node up. `meshbay-hub prune-groups` collects the
ones that never got a node, meant for cron, with --dry-run. The migration
backfills hosted_at from created_at: without that the first run would have
deleted every live group.
Presence rides on the group list itself, read from the signaling registry the
hub already keeps — no poll, no timer. It says a node is connected *to the hub*,
which is not a promise that this browser can reach it and not something a
dishonest hub could not fake; the client downgrades it on a connection it tried
and failed, which is the evidence that concerns the reader.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Diffstat (limited to 'packages/meshbay-hub/src/meshbay_hub/db')
| -rw-r--r-- | packages/meshbay-hub/src/meshbay_hub/db/migrations/versions/3dc91cd4ea52_group_hosted_at.py | 37 | ||||
| -rw-r--r-- | packages/meshbay-hub/src/meshbay_hub/db/models.py | 5 |
2 files changed, 42 insertions, 0 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/db/migrations/versions/3dc91cd4ea52_group_hosted_at.py b/packages/meshbay-hub/src/meshbay_hub/db/migrations/versions/3dc91cd4ea52_group_hosted_at.py new file mode 100644 index 0000000..57aa9a7 --- /dev/null +++ b/packages/meshbay-hub/src/meshbay_hub/db/migrations/versions/3dc91cd4ea52_group_hosted_at.py @@ -0,0 +1,37 @@ +"""group hosted_at + +Records the first time a node announced that it hosts a group. Groups without +it are shown to their owner only, and `meshbay-hub prune-groups` collects the +ones that never got a node. + +Backfilled to created_at for every existing group: they predate the rule, and +starting the clock on them retroactively would delete live groups on the first +run of the reaper. + +Revision ID: 3dc91cd4ea52 +Revises: d1f47a90c3b2 +Create Date: 2026-08-16 03:20:00.575130 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision: str = '3dc91cd4ea52' +down_revision: Union[str, Sequence[str], None] = 'd1f47a90c3b2' +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + op.add_column("groups", + sa.Column("hosted_at", sa.DateTime(timezone=True), nullable=True)) + # Existing groups are grandfathered in rather than left to expire. + op.execute("UPDATE groups SET hosted_at = created_at WHERE hosted_at IS NULL") + + +def downgrade() -> None: + op.drop_column("groups", "hosted_at") diff --git a/packages/meshbay-hub/src/meshbay_hub/db/models.py b/packages/meshbay-hub/src/meshbay_hub/db/models.py index 7d5a3e3..a220a64 100644 --- a/packages/meshbay-hub/src/meshbay_hub/db/models.py +++ b/packages/meshbay-hub/src/meshbay_hub/db/models.py @@ -99,6 +99,11 @@ class Group(Base): description: Mapped[str | None] = mapped_column(String(512)) status: Mapped[str] = mapped_column(String(16), default="active") # active|suspended|revoked created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=_now) + # First time a node registered on /v1/nodes/ws announcing that it hosts this + # group. Until then the group has no files, no key and nobody to serve it, so + # it is shown to its owner only and is what `prune-groups` collects. Set once + # and never cleared: a node going offline does not un-host a group. + hosted_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True)) members: Mapped[list["GroupMember"]] = relationship(back_populates="group") |