Skip to content

Manifest format

vibebeacon.json version 1. This is the only thing we accept, and the only thing we hold. If a field is not on this page, the validator rejects it.

A complete example

vibebeacon.json
{
  "schemaVersion": 1,
  "generator": { "name": "vibebeacon-cli", "version": "0.1.0" },
  "generatedAt": "2026-09-03T10:00:00Z",
  "app": {
    "name": "My Coffee Shop POS",
    "description": "Orders and payments for one cafe",
    "kind": "web",
    "hosting": ["vercel", "supabase"],
    "aiTools": ["cursor", "claude-code"]
  },
  "runtimes": [
    { "name": "node", "version": "22.11.0" }
  ],
  "frameworks": [
    { "name": "next", "version": "15.1.0", "ecosystem": "npm" }
  ],
  "dependencies": [
    { "name": "react", "version": "18.3.1", "ecosystem": "npm", "direct": true, "dev": false },
    { "name": "stripe", "version": "17.4.0", "ecosystem": "npm", "direct": true, "dev": false }
  ],
  "services": [
    { "name": "stripe" },
    { "name": "supabase" }
  ],
  "infrastructure": [
    { "name": "postgres", "version": "16" }
  ]
}

Top level

FieldTypeRequiredNotes
schemaVersionnumberYesAlways 1 today. We reject anything we do not understand rather than guessing.
generatorobjectYesname and version of whatever produced the file. Use "manual" if a person or an AI tool wrote it by hand.
generatedAtstringYesISO 8601 in UTC. Used to work out manifest age and to nudge you when it goes stale.
appobjectYesDescribes the app itself. See below.
runtimesarrayNoLanguage and platform runtimes, such as node or python.
frameworksarrayNoFrameworks worth calling out separately, such as next or django.
dependenciesarrayNoPackages. Up to 2000 entries.
servicesarrayNoThird-party services you call, such as stripe or supabase. Name only.
infrastructurearrayNoDatabases, proxies, container bases, such as postgres 16 or docker.

The app object

FieldTypeRequiredNotes
app.namestringYes1 to 80 characters. Slugged to identify the app. Uploading the same slug updates that app.
app.descriptionstringNoUp to 300 characters. Only for your own benefit in the dashboard.
app.kindenumYesweb, api, mobile, desktop, cli or other.
app.hostingstring[]NoProvider names such as vercel or fly. Free text up to 40 characters each.
app.aiToolsstring[]NoWhich tools built it, such as cursor or lovable. Used for aggregate stats only.

Component entries

Entries in runtimes, frameworks, dependencies, services and infrastructure share one shape. Services often carry a name and nothing else, which is fine.

FieldTypeRequiredNotes
namestringYesPackage or runtime name, lower-cased on our side before matching.
versionstringYesExact resolved version where possible, such as 18.3.1.
ecosystemenumYesRequired on dependencies and frameworks. Runtimes, services and infrastructure do not carry one.
directbooleanNoTrue if it is in your package file rather than pulled in by something else. Defaults to true.
devbooleanNoTrue for dev-only dependencies. Alerts on these are suppressed by default.
resolvedbooleanNoSet false when the version came from a range rather than a lockfile. We then match conservatively.

Ecosystems we accept

npm, PyPI, Go, crates.io, RubyGems, Packagist, Maven, NuGet, Hex, Pub. These are spelled the way OSV spells them, which is why the capitals look inconsistent.

Versions

  • Give us the exact resolved version from the lockfile wherever you can.
  • A range like ^18.2.0 is stripped to 18.2.0 and the entry is marked "resolved": false.
  • Unresolved entries only produce alerts when every version in the range is affected, so you get fewer false alarms and occasionally a missed match.
  • npm, Go and crates versions are compared with semver. PyPI uses PEP 440 comparison.

Limits

  • 2000 dependencies.
  • 512 KB for the whole file.
  • 80 characters for the app name, 300 for the description, 40 per hosting entry.
  • Sixty uploads a minute per API key.

What gets rejected

Secret-shaped strings

Every string is scanned before anything is stored. AWS key shapes, sk_live_ and ghp_ prefixes, JWT shapes, runs of 32 or more hex or base64 characters, password=, and credentials inside URLs such as ://user:pass@. A match means the whole upload is refused with an error naming the field. Nothing is stored, and the value is not written to our logs.
  • Unknown top-level fields. We do not silently keep extras.
  • A schemaVersion we do not support.
  • Absolute or relative file paths anywhere in a value.
  • Repository URLs and database names.

The reasoning behind all of this is on the privacy page.

Versions of the format itself

Version 1 is what exists today. Version 2 will add an optional components array so you can import a CycloneDX SBOM if you already produce one. Version 1 files will keep working.