Problem Statement
Every user's agent strategy today is private and independent — there's no way for a new or uncertain user to benefit from another user's proven configuration, and no reason for a successful user to stick around and engage beyond their own portfolio. A marketplace where users opt in to publish an anonymized track record, and others can follow that strategy's configuration (never its funds), turns the product into something with network effects instead of a single-player tool.
Current State
Strategies are configuration objects implementing RebalanceStrategy (src/agent/strategies.ts), selected per-user via src/agent/router.ts. Performance data already exists in AgentLog and YieldSnapshot, and issue #225 is separately adding richer live-portfolio analytics (Sharpe ratio, drawdown) — this feature should consume whatever ranking metric #225 lands, rather than inventing a third definition of risk-adjusted return.
Proposed Solution
Data Model
model PublishedStrategy {
id String @id @default(uuid())
userId String
label String
strategyConfig Json // serialized RebalanceStrategy config (type + params, no PII)
isPublished Boolean @default(false)
publishedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
followers StrategyFollow[]
@@index([isPublished])
}
model StrategyFollow {
id String @id @default(uuid())
followerUserId String
publishedStrategyId String
followedAt DateTime @default(now())
unfollowedAt DateTime?
follower User @relation(fields: [followerUserId], references: [id], onDelete: Cascade)
publishedStrategy PublishedStrategy @relation(fields: [publishedStrategyId], references: [id], onDelete: Cascade)
@@unique([followerUserId, publishedStrategyId])
}
API Surface
POST /api/agent/strategies/publish — publish/update the caller's own strategy snapshot (requireAuth, no :userId param needed since it always acts on the caller).
POST /api/agent/strategies/unpublish — stop showing it on the marketplace (existing followers keep their current applied config until they explicitly unfollow — see Edge Cases).
GET /api/agent/strategies/marketplace?sortBy=apy|sharpe&window=30d|90d|1y — public leaderboard, paginated, no PII (requireAuth still recommended even for a "public" leaderboard, consistent with the rest of the API's auth posture, but no ownership check needed since it's not user-scoped).
POST /api/agent/strategies/:id/follow / POST /api/agent/strategies/:id/unfollow — requireAuth.
Integration Points
- Following a strategy means the follower's
router.ts selection picks up the published strategyConfig on its next scheduled run — it does not move funds, create a shared wallet, or grant the publisher any access to the follower's CustodialWallet/Position. This boundary must be enforced structurally, not just documented: the follow relationship should have no code path that touches another user's wallet or Stellar keys.
- Anonymization:
PublishedStrategy.label and any displayed performance stats must be derived only from the publishing user's own AgentLog/YieldSnapshot aggregates, never raw balances — build the leaderboard query to select only the aggregate fields it needs, not a broad include that could leak more than intended.
- When a publisher updates their strategy config materially, dispatch a webhook/WhatsApp notification to current followers via the existing
dispatchWebhookEvent/src/whatsapp/formatters.ts paths (new event, e.g. strategy.updated) — a follower's risk exposure should never change silently underneath them.
Edge Cases & Failure Modes
- Publisher unpublishes or deletes their account while others still follow it — existing followers keep their last-applied config (a snapshot at follow time, or at last update) rather than having their agent loop break; they should be notified that the source strategy is no longer being updated.
- A user tries to follow their own published strategy — either a no-op or explicitly disallowed; decide and enforce one, don't leave it undefined.
- Leaderboard gaming: a strategy with very little history (e.g. one day of data) posting a misleadingly high short-window APY — require a minimum track-record length before a strategy is eligible for the leaderboard.
Security & Privacy Considerations
- This is the first feature exposing one user's data (even anonymized) to others — the anonymization boundary is the core security property and needs its own explicit test coverage (a test asserting the marketplace endpoint's response shape contains no
userId, wallet address, or absolute balance fields).
- Consent must be explicit and revocable —
unpublish must be immediate and should stop the strategy appearing in new queries right away, even if existing followers retain their last snapshot.
Out of Scope
- Any transfer of funds, shared custody, or fee-sharing between publisher and follower — configuration-following only.
- Comments/social features beyond the leaderboard itself.
Suggested Implementation Plan
PublishedStrategy/StrategyFollow models, publish/unpublish flow with anonymization enforced at the query layer.
- Follow/unfollow endpoints; wire
router.ts to apply a followed config.
- Leaderboard endpoint with a minimum-track-record eligibility rule.
- Change-notification webhook/WhatsApp event for followers.
Acceptance Criteria
Problem Statement
Every user's agent strategy today is private and independent — there's no way for a new or uncertain user to benefit from another user's proven configuration, and no reason for a successful user to stick around and engage beyond their own portfolio. A marketplace where users opt in to publish an anonymized track record, and others can follow that strategy's configuration (never its funds), turns the product into something with network effects instead of a single-player tool.
Current State
Strategies are configuration objects implementing
RebalanceStrategy(src/agent/strategies.ts), selected per-user viasrc/agent/router.ts. Performance data already exists inAgentLogandYieldSnapshot, and issue #225 is separately adding richer live-portfolio analytics (Sharpe ratio, drawdown) — this feature should consume whatever ranking metric #225 lands, rather than inventing a third definition of risk-adjusted return.Proposed Solution
Data Model
API Surface
POST /api/agent/strategies/publish— publish/update the caller's own strategy snapshot (requireAuth, no:userIdparam needed since it always acts on the caller).POST /api/agent/strategies/unpublish— stop showing it on the marketplace (existing followers keep their current applied config until they explicitly unfollow — see Edge Cases).GET /api/agent/strategies/marketplace?sortBy=apy|sharpe&window=30d|90d|1y— public leaderboard, paginated, no PII (requireAuthstill recommended even for a "public" leaderboard, consistent with the rest of the API's auth posture, but no ownership check needed since it's not user-scoped).POST /api/agent/strategies/:id/follow/POST /api/agent/strategies/:id/unfollow—requireAuth.Integration Points
router.tsselection picks up the publishedstrategyConfigon its next scheduled run — it does not move funds, create a shared wallet, or grant the publisher any access to the follower'sCustodialWallet/Position. This boundary must be enforced structurally, not just documented: the follow relationship should have no code path that touches another user's wallet or Stellar keys.PublishedStrategy.labeland any displayed performance stats must be derived only from the publishing user's ownAgentLog/YieldSnapshotaggregates, never raw balances — build the leaderboard query to select only the aggregate fields it needs, not a broadincludethat could leak more than intended.dispatchWebhookEvent/src/whatsapp/formatters.tspaths (new event, e.g.strategy.updated) — a follower's risk exposure should never change silently underneath them.Edge Cases & Failure Modes
Security & Privacy Considerations
userId, wallet address, or absolute balance fields).unpublishmust be immediate and should stop the strategy appearing in new queries right away, even if existing followers retain their last snapshot.Out of Scope
Suggested Implementation Plan
PublishedStrategy/StrategyFollowmodels, publish/unpublish flow with anonymization enforced at the query layer.router.tsto apply a followed config.Acceptance Criteria
CustodialWalletor triggers any Stellar operation on their behalfdocs/openapi.yamlupdated