Effort 2: make data ownership per-user (feeds, aggregates, OPML) #65
Labels
No labels
Compat/Breaking
Kind/Bug
Kind/Documentation
Kind/Enhancement
Kind/Feature
Kind/Security
Kind/Testing
Priority
Critical
Priority
High
Priority
Low
Priority
Medium
Reviewed
Confirmed
Reviewed
Duplicate
Reviewed
Invalid
Reviewed
Won't Fix
Status
Abandoned
Status
Blocked
Status
Need More Info
No milestone
No assignees
1 participant
Notifications
Due date
No due date set.
Reference
marvin8/yunjin#65
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Part of #25 (Phase 6 multi-user). Effort 1 added authentication, sessions, and CSRF, but the data behind the login is still single-owner: there is one global feed list, one global set of articles and aggregates, and OPML import/export and the CLI act on the whole instance. A second account logging in sees and manages the same feeds and topics as the first.
Yunjin committed to fully independent users: each account gets its own feeds, OPML, and aggregates. Making ownership a schema-level concern touches the feed table shape, the fetch worker, the LLM aggregation pipeline (which has a per-run API cost), the OPML flows, and an Alembic migration so existing single-user deployments keep working with their data assigned to the seeded default user.
Design brainstormed and approved 2026-10-03; implementation plan to follow.
Design findings (brainstormed with Marvin8 2026-10-03, spec at
.superpowers/specs/2026-10-03-effort2-ownership-design.md, git-ignored):yunjin fetch/aggregaterun on a fixed cadence from the container worker entry (container-entrypoint.sh, default hourly).feedsglobal, addfeed_subscriptions(user_id, feed_id)— duplicate subscriptions must not duplicate fetches/full-text/media/storage, andarticles.url UNIQUEmust survive.user_id; read-path queries scope to the session user; cross-user aggregate ids 404.20261002_add_user_credentials; everything existing backfills to user 1 (default).Plan of attack
feed_subscriptionstable +aggregates.user_id NOT NULL(table rebuild), backfill to user 1.db/subscriptions.py(pure SQL CRUD) +FeedSubscriptiondataclass;Aggregate.user_id.services/subscriptions.py: subscribe-or-create-feed rule; unsubscribe flow with per-user aggregate-link cleanup and orphan-feed GC (last subscriber out deletes feed; FK cascades take articles/media/tags/read-state).LlmAggregationService: per-user loop, watermark + backlog rule, per-user TTL expiry, refresh isolation, failure isolation.fetch_all_feedsneeds no change (GC invariant: feeds rows ≡ union of subscriptions).--useron add-feed/list-feeds (defaultdefault); docs (docs/cli.md,docs/reader.md) in the same PR.TDD throughout;
uv run noxgreen before every push. Work proceeds onfeat/issue-65-data-ownershipcut fromfeat/multiuser; sub-PR targetsfeat/multiuser(COLLECTING BRANCH), Refs #25.Landed on
mainin release 2026.10.6.1 (PR #69, merge89f26f7). Closing.