-
Notifications
You must be signed in to change notification settings - Fork 0
Open
Description
Overview
Implement anonymous subscriber tracking to prioritize feed updates based on popularity. The CLI should update feeds with more iOS app subscribers more frequently.
Architecture
iOS App (clients)
↓ subscribe/unsubscribe
FeedSubscription records (source of truth)
↓ aggregated by CLI
Feed.subscriberCount (cached for performance)
↓ used for prioritization
CLI update command (--update-min-popularity, sorting)
Implementation Tasks
1. Add FeedSubscription Record Type to Schema
Update schema.ckdb:
// FeedSubscription - Anonymous feed subscription tracking
RECORD TYPE FeedSubscription (
"___recordID" REFERENCE QUERYABLE, // SHA256(userID + feedID)
"feedRecordName" STRING QUERYABLE, // Feed record name
// Permissions: Users can manage their own subscriptions
GRANT READ TO "_world",
GRANT CREATE, WRITE, DELETE TO "_icloud"
);
Note: The record ID is a SHA256 hash of userID + feedID to ensure:
- Same user + same feed = same record (prevents duplicates across devices)
- Can't correlate subscriptions across different feeds (privacy)
- One-way hash (can't reverse to get user ID)
Deploy schema: ./Scripts/setup-cloudkit-schema.sh
2. Add FeedSubscription Model to CelestraKit
Create the model in CelestraKit package (shared between CLI and iOS app):
public struct FeedSubscription: Sendable {
public let recordName: String
public let feedRecordName: String
// Helper to generate hashed record ID
public static func subscriptionRecordID(
for feedRecordName: String,
userRecordID: String
) -> String {
let input = "\(feedRecordName)-\(userRecordID)"
let hash = SHA256.hash(data: Data(input.utf8))
return hash.compactMap { String(format: "%02x", $0) }.joined()
}
}3. Implement sync-subscriber-counts CLI Command
Create Sources/CelestraCloud/Commands/SyncSubscriberCountsCommand.swift:
Algorithm:
- Query all
FeedSubscriptionrecords - Group by
feedRecordNameand count - Batch update
Feed.subscriberCountfields (non-atomic, batches of 10) - Log results (feeds updated, total subscribers counted)
Command usage:
celestra-cloud sync-subscriber-counts4. Add CloudKit Service Methods
In Sources/CelestraCloudKit/Services/CloudKitService+Celestra.swift:
// Query all subscription records
func queryAllSubscriptions() async throws -> [FeedSubscription]
// Batch update subscriber counts on feeds
func updateSubscriberCounts(_ counts: [String: Int]) async throws -> BatchOperationResult5. Update Workflow Integration
In .github/workflows/update-feeds.yml, run sync before updates:
- name: Sync subscriber counts
run: |
source .env
celestra-cloud sync-subscriber-counts
- name: Update feeds (prioritized by popularity)
run: |
source .env
celestra-cloud update --update-delay ${{ env.UPDATE_DELAY }}6. Enhance Feed Update Prioritization
Update UpdateCommand.swift to sort by subscriber count:
let feeds = try await service.queryFeeds(
filters: filters,
sortBy: [
.descending("subscriberCount"), // Popular feeds first
.ascending("attemptedTimestamp") // Then by staleness
],
limit: limit
)7. iOS App Integration (Future Work)
Document how iOS app should create/delete subscriptions:
// Subscribe to feed
func subscribe(to feedRecordName: String) async throws {
let userRecordID = try await CKContainer.default().userRecordID()
let recordID = FeedSubscription.subscriptionRecordID(
for: feedRecordName,
userRecordID: userRecordID.recordName
)
let record = CKRecord(
recordType: "FeedSubscription",
recordID: CKRecord.ID(recordName: recordID)
)
record["feedRecordName"] = feedRecordName
try await CKContainer.default().publicCloudDatabase.save(record)
}
// Unsubscribe from feed
func unsubscribe(from feedRecordName: String) async throws {
let userRecordID = try await CKContainer.default().userRecordID()
let recordID = FeedSubscription.subscriptionRecordID(
for: feedRecordName,
userRecordID: userRecordID.recordName
)
try await CKContainer.default().publicCloudDatabase.deleteRecord(
withID: CKRecord.ID(recordName: recordID)
)
}Benefits
- iOS app: Fast subscription management (create/delete one record)
- CLI: Efficient filtering and sorting without counting per-feed
- Users: Popular feeds update more frequently
- Privacy: Anonymous tracking via hashed record IDs
- Cost: Minimal CloudKit queries
Testing Plan
- Add FeedSubscription test data in development environment
- Run
sync-subscriber-countsand verify Feed.subscriberCount updated - Run
update --update-min-popularity 5and verify filtering works - Verify feeds are processed in descending subscriber count order
References
- Privacy-preserving design using SHA256 hashing
- Eventual consistency model (counts synced periodically)
- Non-atomic batch updates for resilience
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels