Sharing a streamline by link — listing.json

Sharing a streamline by link: listing.json, what the app computes, signed versions

Raw Markdown the file as the skill ships it All pages ↓

An organization shares its own streamlines at an unguessable link, https://streamliner.work/s/<id>. Owners and admins do it from the streamline's Library editor (Share Link). The link belongs to the organization: its page says Shared by <organization>, and any of the organization's owners and admins can stop it. Members don't share. Personal streamlines aren't shared by link (move one to an organization first); a .streamline.json file can still be sent by hand. Anyone with the link sees a listing page and taps Get to add it to On this device. Links aren't searchable or reviewed; there is no public catalog yet.

Two switches decide it. The streamline opts in, in its private half, and stays unshareable until an owner or admin turns it on and publishes:

// private/agents/<kind>.json
{ "sharing": { "link": true } }

The organization can turn sharing off for every streamline (private/tenant.json discover: { sharing: "off" }; the default is "admins"). Keep streamlines like payroll off.

What a shared copy is. A recipient's copy always runs on the recipient's own model key, never the organization's (the relay and the organization's key serve invited members only). So the app builds the shared file in the personal format (personal-streamline.md) from both published halves: the instructions (briefs written in first), the model, the servers and their tool rules (a tool that waits for a tap still does; the allowlist becomes tools.allow; deny becomes hidden), the cards, welcome and quick phrases. It leaves out what never leaves the account: credentialRef and any key reference, roles (and tools only some roles may use), members and the directory, network rules, budgets and limits, the fallback model, sealed forms and views, the overview, and everything in private/tenant.json. configcheck warns sharing.leftOut when a shareable streamline relies on one of those. Before the first share, and before any version that widens what runs without a tap, the app shows what becomes public and asks for Face ID. The server can't read the opt-in (the shared file has no private half), so the app enforces it; the server decides who may publish (owners and admins) and scans every file for keys.

This page is the format, so you can prepare and check a listing folder.

Contents

  • The folder
  • listing.json
  • What the app computes, never you
  • How a version is signed
  • Checklist

The folder

find-a-flight/
  listing.json
  kiwi-flights.streamline.json      its "id" is the one listing.json names

Check it with node scripts/check.mjs find-a-flight/ (configcheck): the schema, and that every id in streamlines names a streamline file beside it (listing.streamline-missing).

listing.json

{
  "$schema": "https://streamliner.work/schema/v1/listing.schema.json",
  "schema": "streamline.listing/1",
  "name": "Find a Flight",
  "subtitle": "Kiwi.com · Made by Streamliner, not by Kiwi.com",
  "summary": "Say where and when. Get flights with prices and a button to book.",
  "streamlines": ["kiwi_flights"],
  "version": "1.0",
  "releaseNotes": "First version.",
  "service": { "name": "Kiwi.com", "domain": "kiwi.com" },
  "visibility": "link",
  "ageRating": "4+"
}
FieldReqNotes
schemayesstreamline.listing/1
nameyes≤ 60 characters; usually the streamline's name
subtitleno≤ 80; say Made by …, not by <Company> when you built it for someone else's product
summaryyes≤ 300, plain text
streamlinesyesexactly one id for a link share (a pack waits for the catalog; listing.pack warns)
versionyeswhat people see: 1, 1.2, 1.2.3
releaseNotesno≤ 2000
support.urlnohttps; never a sign-up or pricing page
serviceno{ name, domain }; never set claimed (only Streamliner does, after a domain check)
visibilitynolink; public is refused until review exists
ageRatingno4+, 9+, 12+, 17+
categorynobusiness, money, work, developers, everyday (for the catalog, later)

What the app computes, never you

The listing page's label (what it connects to, whether you sign in, what runs without asking, what asks first, which links buttons may open) comes from the streamline file itself, not from listing.json. Write the tool policy honestly in the streamline (tool-policy.md); the label shows it. scripts/listing-label.mjs in the repo prints the same label.

How a version is signed

The app signs each version with the device's own sharing key: a manifest (streamline.listing-version/1) listing listing.json and streamlines/<id>.streamline.json with their sha256, signed Ed25519 over its JCS bytes. The listing's channel is the key's fingerprint, so a reader checks the signature and the channel without trusting the server. Sharing again publishes what the organization has published since, as the next version at the same link, from the device that first shared it. Stop Sharing (any owner or admin) ends the link; copies people already got keep working.

Checklist

  • [ ] The streamline's private half says "sharing": { "link": true }, published.
  • [ ] One streamline, its id set, and the listing names it.
  • [ ] No key, token or sign-in anywhere (the server refuses a key-shaped string: has_secret).
  • [ ] Every MCP server is https://.
  • [ ] configcheck is clean on the folder.