Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 18 additions & 3 deletions docs/contributing/architecture/data-storage.md
Original file line number Diff line number Diff line change
Expand Up @@ -186,8 +186,10 @@ The schema is defined by migrations in `packages/worker/migrations/`:

- `users`: login identity and password hash, plus the persisted stable MCP
`userId` (`stable_user_id`, SHA-256 of the normalized email, unique partial
index from migration 0052; written at signup). Inbound email routing does not
reverse-resolve stable ids at all — it uses the indexed username lookup
index from migration 0052; written at signup). Optional community profile
fields (`display_name`, `bio`, `profile_visibility` with default `public`)
come from migration 0065. Inbound email routing does not reverse-resolve
stable ids at all — it uses the indexed username lookup
(`findPublicUserIdentityByUsername`). The remaining contextless paths resolve
stable ids with one indexed point read (`findUserRowByStableUserId` /
`findUserAccountByStableUserId`); legacy rows with a NULL `stable_user_id`
Expand All @@ -208,7 +210,20 @@ The schema is defined by migrations in `packages/worker/migrations/`:
- `saved_packages`: package metadata/search projection derived from published
`package.json` source, plus a user-scoped `hidden` flag (0/1) that excludes
the package from default ranked search while leaving list/get/execute paths
intact
intact, and `is_private` (0/1, migration 0065) projecting
`package.json#private` for public-profile and timeline filters (migration
defaults existing rows to private;
`POST /__maintenance/backfill-package-privacy` recomputes from manifests)
- `community_listings`, `community_forks`, `community_ratings`,
`community_reports`, `community_bans`: public community package listings and
moderation (see [Community packages](../community-packages.md))
- `user_follows`: follow edges between MCP stable user ids (`follower_user_id` /
`followee_user_id`)
- `community_stars`: listing stargazer bookmarks (`listing_id` + `user_id`),
distinct from 1–5 `community_ratings`
- `community_activity_events`: stored `listing_published` / `listing_updated`
timeline events (`actor_user_id` + `listing_id`); fork and star timeline items
are derived at read time from `community_forks` / `community_stars`
- `secret_buckets`: encrypted-secret ownership buckets scoped to `user`,
`package`, or `session`. Package buckets bind directly to `saved_packages.id`;
package runtimes may use their own package secrets, while user secrets require
Expand Down
20 changes: 20 additions & 0 deletions docs/contributing/architecture/primitives.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,19 @@ primitives:
- docs/use/community-packages.md
- docs/contributing/community-packages.md

- id: community-social
group: assistant
name: Community profiles and social graph
summary:
Public profiles, follows, timelines, and listing stargazer bookmarks.
code:
- packages/worker/src/community/social-repo.ts
- packages/worker/src/community/social-service.ts
- packages/worker/migrations/0068-community-social.sql
docs:
- docs/use/community-profiles.md
- docs/contributing/community-packages.md

- id: package-apps
group: assistant
name: Package apps
Expand Down Expand Up @@ -483,6 +496,13 @@ invariants:
- docs/use/community-packages.md
- docs/contributing/community-packages.md

- id: community-social-privacy
summary:
Private profiles and private packages stay off public social surfaces.
docs:
- docs/use/community-profiles.md
- docs/contributing/community-packages.md

- id: no-secrets-in-chat
summary: Raw secrets never appear in prompts, chat, results, or app source.
docs:
Expand Down
76 changes: 61 additions & 15 deletions docs/contributing/community-packages.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,37 @@ Migration: `packages/worker/migrations/0045-community-listings.sql`
| `community_reports` | Reports with denormalized `listing_name` / `listing_owner_user_id` |
| `community_bans` | Community-wide bans (publish, fork, rate, report) |

Social / profiles migration:
`packages/worker/migrations/0068-community-social.sql`

| Table / column | Purpose |
| --------------------------------- | ----------------------------------------------------------------- |
| `users.display_name`, `users.bio` | Optional public profile fields |
| `users.profile_visibility` | `public` (default) or `private` |
| `saved_packages.is_private` | Projection of `package.json#private` for profile/timeline filters |
| `user_follows` | Follow edges keyed by MCP stable user ids |
| `community_stars` | Listing stargazers (bookmark stars; not 1–5 ratings) |
| `community_activity_events` | Stored `listing_published` / `listing_updated` events only |

Follow, star, and activity actor columns store the MCP **stable user id**
(`users.stable_user_id`), matching other community ownership columns such as
`community_listings.owner_user_id`.

`saved_packages.is_private` defaults to `1` in the migration (safe until
manifests are read). Package save/publish paths keep the column in sync with
`package.json#private`. Operators can recompute every row with
`POST /__maintenance/backfill-package-privacy` (Bearer
`CAPABILITY_REINDEX_SECRET`), implemented in `maintenance-handler.ts`.

### Derived timeline events

Timelines merge stored `community_activity_events` with **read-time derived**
fork and star items from `community_forks` / `community_stars`. Forks appear
only while the forker's saved package copy has `is_private = 0`; unstarring
drops the star item immediately. Ratings are never projected into timelines.
Storing only publish/update events avoids orphaned fork/star timeline rows when
privacy flips or a star is removed — see `social-repo.ts` / `social-service.ts`.

`community_listings` enforces one listing per `(owner_user_id, package_id)`.
Admin **delist** sets `status = 'delisted'`, blocks owner re-publish, and blocks
owner unpublish. **Hard delete** (admin report action) removes the listing row,
Expand Down Expand Up @@ -120,15 +151,17 @@ icon entries for the listing.

Core logic: `packages/worker/src/community/`

| Module | Role |
| -------------- | ------------------------------------------------------------------- |
| `service.ts` | Publish, unpublish, search, fork, rate, report, admin resolution |
| `install.ts` | One-click install: fork + publish checks + projection publish |
| `repo.ts` | D1 queries |
| `snapshot.ts` | KV snapshot I/O |
| `fork-scan.ts` | Manifest rewrite + cross-scope `kody:@…` / `kody.dependencies` scan |
| `og-image.ts` | Community listing 1200×630 PNG on the shared `#worker/og` pipeline |
| `types.ts` | Shared record types |
| Module | Role |
| ------------------- | ------------------------------------------------------------------- |
| `service.ts` | Publish, unpublish, search, fork, rate, report, admin resolution |
| `social-service.ts` | Profiles, follows, timeline merge, stars / stargazers |
| `social-repo.ts` | D1 for profiles, follows, stars, activity, public package lists |
| `install.ts` | One-click install: fork + publish checks + projection publish |
| `repo.ts` | D1 queries |
| `snapshot.ts` | KV snapshot I/O |
| `fork-scan.ts` | Manifest rewrite + cross-scope `kody:@…` / `kody.dependencies` scan |
| `og-image.ts` | Community listing 1200×630 PNG on the shared `#worker/og` pipeline |
| `types.ts` | Shared record types |

`publishCommunityListing` validates MIT license, README `## Intent`, published
commit, and ban status; copies published source into KV; upserts D1 metadata.
Expand Down Expand Up @@ -166,6 +199,10 @@ Capabilities:
- `community_get`
- `community_fork`
- `community_rate`
- `community_star` / `community_unstar` / `community_starred_list`
- `community_profile_get` / `community_profile_update`
- `community_follow` / `community_unfollow`
- `community_timeline`
- `community_report`
- `community_set_trusted` (admin-only via `requiredRole`)
- `community_set_featured` (admin-only via `requiredRole`)
Expand Down Expand Up @@ -253,17 +290,26 @@ Empty queries sort by Bayesian score, then `publishedAt`.
## Isolation invariants

- Forks are copies; **no cross-user `kody:@…` import ever resolves**.
- The only deliberate cross-user data flows are public listing snapshots and
aggregate ratings.
- Owner `userId` is not exposed on public pages or community capability
responses; package name scope reveals the owner's username by design.
- Deliberate cross-user data flows are public listing snapshots, aggregate
ratings, star counts/stargazers, and public profile / timeline surfaces.
- Private profiles and private packages (`profile_visibility = private`,
`saved_packages.is_private = 1`) must not appear on public social reads.
- Package name scope and public profiles reveal the owner's username by design;
browsing does not require exposing a stable owner user id on every search hit.
- Community results stay out of general MCP `search` and per-user package vector
indexes.

Account deletion and export cover social tables through `accountUserDataTargets`
(`user_follows` on both follower and followee columns, `community_stars` by user
and by owned listing, `community_activity_events` by actor and by owned
listing), matching the multi-column pattern used for `community_reports`.

## Related docs

- [Community profiles (usage)](../use/community-profiles.md) — agent-facing
profiles, follows, timelines, and stars
- [Packages and manifests](./packages-and-manifests.md) — saved package model
- [Repo-backed editing sessions](../use/repo-sessions.md) — fork activation path
- [Adding capabilities](./adding-kody.md) — domain registration
- [Primitives map](./architecture/primitives.yaml) — `community-listings`
primitive entry
- [Primitives map](./architecture/primitives.yaml) — `community-listings` and
`community-social` primitive entries
34 changes: 26 additions & 8 deletions docs/use/community-packages.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,10 @@ delete can remove the delisted row.
## Browsing listings

Anyone can browse `/community` and open `/community/:listingId`. Detail pages
show metadata, aggregate ratings, fork count, the README (not the full source
tree), and a dynamically generated Open Graph image (1200×630).
show metadata, aggregate ratings, **star count** (stargazers — distinct from 1–5
ratings), fork count, the README (not the full source tree), and a dynamically
generated Open Graph image (1200×630). When the owner keeps a
[public profile](./community-profiles.md), the listing links to `/@username`.

Each detail page includes a **copyable prompt** you can hand to your agent to
start a fork. You can also ask your agent to use `community_search` or
Expand Down Expand Up @@ -170,6 +172,14 @@ Featured listings show a **Featured** badge on their detail page. Admins toggle
featuring from the listing detail page or with the `community_set_featured`
capability.

## Stars (stargazers)

Anyone signed in can **star** a listing as a public bookmark (`community_star` /
`community_unstar`). Star counts appear on search and detail outputs; stargazer
lists include only users with public profiles. Stars are separate from the 1–5
**ratings** below — see
[Community profiles](./community-profiles.md#stars-vs-ratings).

## Ratings

After forking, your agent can call `community_rate` with:
Expand Down Expand Up @@ -203,21 +213,29 @@ Use the MCP `community` domain:
- `community_publish` — publish or update a listing from a saved package
- `community_unpublish` — remove your active listing (not delisted listings)
- `community_search` — search active listings
- `community_get` — fetch one listing's metadata and aggregates
- `community_get` — fetch one listing's metadata and aggregates (including star
count, stargazers, and owner profile linkage when the owner is public)
- `community_fork` — copy a pinned snapshot into your account (inert until
published)
- `community_rate` — rate a listing after forking
- `community_star` / `community_unstar` — bookmark a listing (see
[Community profiles](./community-profiles.md))
- `community_report` — report a listing (requires login)
- `community_set_trusted` — admin-only: mark or unmark a listing as trusted at
its current pinned commit
- `community_set_featured` — admin-only: feature or unfeature a trusted listing
as an onboarding starter package

Profiles, follows, and timelines use additional `community_*` capabilities
documented in [Community profiles](./community-profiles.md).

## Privacy and isolation

Forks are copies. Cross-user package imports never resolve. The only deliberate
cross-user data flows are the public listing snapshot and aggregate ratings.
Forks are copies. Cross-user package imports never resolve. The deliberate
cross-user data flows are the public listing snapshot, aggregate ratings, star
counts/stargazers, and [public profile](./community-profiles.md) surfaces.

Owner user ids are not exposed on public pages or through community kody. The
package name scope reveals the owner's **username**, as it does for normal
package URLs.
Stable owner **user ids** are not required for browsing: package name scope and
public profiles reveal the owner's **username** (as package URLs do). Search
summaries may still omit a stable owner id (`owner_anonymous`) while linking by
username when the owner profile is public.
108 changes: 108 additions & 0 deletions docs/use/community-profiles.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
# Community profiles, follows, and stars

Kody users on the same deployment can publish a **public profile**, follow other
public profiles, browse a personal **timeline** of followees' public activity,
and **star** community listings (bookmarking, distinct from 1–5 star ratings).

Profiles and social graphs live in the MCP **`community`** domain alongside
[community packages](./community-packages.md). Private package source stays
isolated; social surfaces only show content that is deliberately public.

## Public profiles

Each account has profile fields:

- **Display name** — shown on the profile and activity items (falls back to
username when unset)
- **Bio** — short public text
- **Avatar** — optional profile image (PNG, JPEG, or WebP)
- **Profile visibility** — `public` by default, or `private`

Public profiles are at `/@username`. A public profile shows display name, bio,
avatar, join date, follower and following counts, the user's **public packages**
(metadata only), and recent public activity.

### Avatars

Upload or remove an avatar from **Account → Profile** in the web UI (MCP does
not accept avatar uploads). Accepted formats are PNG, JPEG, and WebP, up to 1
MB, with each side between 64px and 4096px and an aspect ratio of at most 3:1.
Avatars appear on the public profile, in timeline and profile activity rows, and
next to public stargazers on listing pages. Private profiles still keep the
avatar for the owner; other users do not see it.

Package privacy follows `package.json#private` (projected onto
`saved_packages.is_private`):

- Packages with `"private": true` (or missing `private` on create) do not appear
on the public profile.
- Public packages that are **not** community-published expose name, kody id,
description, and tags only — never README or source, and they cannot be forked
from the profile.
- Community-published packages on the profile carry a listing signifier and a
fork affordance (same inert-fork rules as
[community packages](./community-packages.md#forking-a-listing)).

### Private mode

When visibility is `private`:

- `/@username` and public profile reads return not found (404)
- The user cannot be followed
- They do not appear in public stargazer lists
- Their activity does not appear on other users' timelines

The account owner can still read and update their own profile (including while
private) through `community_profile_get` / `community_profile_update`.

## Following and the timeline

Signed-in users can follow public profiles with no approval step. Private
profiles reject follows.

`/timeline` (signed-in) lists public activity from accounts you follow, newest
first. Timeline item types:

| Type | Source |
| ----------------- | -------------------------------------------------------------- |
| Listing published | Stored when a listing is published |
| Listing updated | Stored when a listing is re-published |
| Listing forked | Derived from `community_forks` while the forked copy is public |
| Listing starred | Derived from `community_stars` while the star remains |

Fork items disappear if the forker marks that saved package private again.
Unstarring removes the star item. **Ratings never appear** on timelines.

## Stars vs ratings

**Stars** (`community_star` / `community_unstar`) are a public stargazer
bookmark on a listing: star counts on listings, a stargazer list (public-profile
users only), and a starred-packages view for the signed-in user.

**Ratings** (`community_rate`) remain the separate 1–5 usefulness /
adaptation-effort scores after forking. They are unchanged and stay off
timelines. See [Ratings](./community-packages.md#ratings).

## Capabilities

Use the MCP `community` domain:

- `community_profile_get` — read a profile by username (own private profile
included when signed in as that user)
- `community_profile_update` — update display name, bio, and visibility
- `community_follow` / `community_unfollow` — follow or unfollow a public
profile
- `community_timeline` — chronological public activity from followees
- `community_star` / `community_unstar` — star or unstar a listing
- `community_starred_list` — list listings the signed-in user has starred

Listing search/get also expose `star_count`; `community_get` can include
stargazers and owner profile linkage for public owners. Package listing
workflows stay in [Community packages](./community-packages.md).

## Privacy

Profile fields are user-controlled public content when visibility is public.
Account [export and deletion](./privacy.md) include follows (either side of an
edge), stars, activity events, and profile columns. Cross-user package source
isolation is unchanged.
2 changes: 2 additions & 0 deletions docs/use/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ Read in order for a full tour, or jump to a topic.
- [Packages](./packages.md)
- [Community packages](./community-packages.md) — share, browse, and fork
published packages on the same deployment
- [Community profiles](./community-profiles.md) — public profiles, follows,
timelines, and listing stars
- [Repo-backed editing sessions](./repo-sessions.md)
- [Raw MCP content blocks](./raw-content-blocks.md)
- [Secrets, values, and host approval](./secrets-and-values.md)
Expand Down
Loading
Loading