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
| Field | Type | Required | Notes |
|---|---|---|---|
| schemaVersion | number | Yes | Always 1 today. We reject anything we do not understand rather than guessing. |
| generator | object | Yes | name and version of whatever produced the file. Use "manual" if a person or an AI tool wrote it by hand. |
| generatedAt | string | Yes | ISO 8601 in UTC. Used to work out manifest age and to nudge you when it goes stale. |
| app | object | Yes | Describes the app itself. See below. |
| runtimes | array | No | Language and platform runtimes, such as node or python. |
| frameworks | array | No | Frameworks worth calling out separately, such as next or django. |
| dependencies | array | No | Packages. Up to 2000 entries. |
| services | array | No | Third-party services you call, such as stripe or supabase. Name only. |
| infrastructure | array | No | Databases, proxies, container bases, such as postgres 16 or docker. |
The app object
| Field | Type | Required | Notes |
|---|---|---|---|
| app.name | string | Yes | 1 to 80 characters. Slugged to identify the app. Uploading the same slug updates that app. |
| app.description | string | No | Up to 300 characters. Only for your own benefit in the dashboard. |
| app.kind | enum | Yes | web, api, mobile, desktop, cli or other. |
| app.hosting | string[] | No | Provider names such as vercel or fly. Free text up to 40 characters each. |
| app.aiTools | string[] | No | Which 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.
| Field | Type | Required | Notes |
|---|---|---|---|
| name | string | Yes | Package or runtime name, lower-cased on our side before matching. |
| version | string | Yes | Exact resolved version where possible, such as 18.3.1. |
| ecosystem | enum | Yes | Required on dependencies and frameworks. Runtimes, services and infrastructure do not carry one. |
| direct | boolean | No | True if it is in your package file rather than pulled in by something else. Defaults to true. |
| dev | boolean | No | True for dev-only dependencies. Alerts on these are suppressed by default. |
| resolved | boolean | No | Set 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.0is stripped to18.2.0and 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
schemaVersionwe 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.