Skip to content
Draft
Show file tree
Hide file tree
Changes from all 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
22 changes: 17 additions & 5 deletions .github/workflows/guard.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Publish guard — this repo must never contain engine material.
# Fails the build if forbidden patterns appear anywhere in the tree.
# Publish guard — this repo must never contain engine material or
# private partner names. Fails the build if forbidden patterns appear
# anywhere in the tree.
name: publish-guard
on: [push, pull_request]
jobs:
Expand All @@ -10,12 +11,23 @@ jobs:
- name: Forbidden-content scan
run: |
set -e
PATTERNS='supabase\.co|SERVICE_ROLE|sk-ant-|sk_live|MODEL_PRICING|pricing\.charge|narrate-|compile-meaning|signals\.ts|governance'
PATTERNS='supabase\.co|SERVICE_ROLE|sk-ant-|sk_live|MODEL_PRICING|pricing\.charge|narrate-|compile-meaning|signals\.ts|governance|shipmemory|posemesh|mentra|auki'
if grep -rniE "$PATTERNS" --exclude-dir=.git --exclude=guard.yml .; then
echo "::error::Forbidden pattern found — engine material must never enter this repo."
echo "::error::Forbidden pattern found — engine material and partner names must never enter this repo."
exit 1
fi
echo "Clean."
- name: Validate examples against schema
- name: Validate capture examples against schema
run: |
npx --yes -p ajv-cli@5 -p ajv-formats ajv validate --spec=draft2020 -c ajv-formats -s schema/capture.schema.json -r schema/location.schema.json -d "examples/*.bevia-capture.json"
- name: Validate export example lines against schema
run: |
set -e
mkdir -p /tmp/export-lines
i=0
while IFS= read -r line; do
[ -z "$line" ] && continue
i=$((i+1))
printf '%s' "$line" > "/tmp/export-lines/line-$i.json"
done < examples/robot-session.bevia-export.jsonl
npx --yes -p ajv-cli@5 -p ajv-formats ajv validate --spec=draft2020 -c ajv-formats -s schema/export-record.schema.json -r schema/location.schema.json -d "/tmp/export-lines/*.json"
48 changes: 31 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,24 +1,31 @@
# Bevia Capture Protocol

The open specification for feeding evidence into a [Bevia](https://bevia.co)
engine. If you can write a JSON file, you can build a Bevia sensor.

- **[SPEC.md](SPEC.md)** — the protocol: capture body, drop-file
convention, location object, behavioral guarantees. Versioned,
additive-only.
# Bevia Protocol

The open specification for the doors of a [Bevia](https://bevia.co)
engine — how evidence goes in, and how understanding comes back out.
If you can read or write a JSON file, you can build against Bevia.

- **[SPEC.md](SPEC.md)** — the capture protocol (v1, the way in):
capture body, drop-file convention, location object, behavioral
guarantees. Versioned, additive-only.
- **[SPEC-EXPORT.md](SPEC-EXPORT.md)** — the export protocol (v1.1,
the way out): export records, drop-file and pull conventions,
provenance guarantees.
- **[schema/](schema/)** — JSON Schemas for validation.
- **[examples/](examples/)** — real capture files: a chat session, a
robot maintenance session, a captured note.
- **[examples/](examples/)** — real capture files (a chat session, a
robot maintenance session, a captured note) and a matching export.
- **[reference/](reference/)** — reference sensor implementations.

## The one-paragraph version

Bevia turns observed work — conversations, sessions, notes — into a
navigable, queryable memory. This repo specifies the **doors**: the
shape of a capture, how a file in a watched folder becomes evidence
(`*.bevia-capture.json` — the file's content *is* the request body),
and how a sensor stamps *where* knowledge happened. Everything here is
open and free to implement, forever.
navigable, queryable memory. This repo specifies the **doors**. In:
the shape of a capture, how a file in a watched folder becomes
evidence (`*.bevia-capture.json` — the file's content *is* the
request body), and how a sensor stamps *where* knowledge happened.
Out: the export record, how the engine's understanding lands as
files or answers a pull (`*.bevia-export.jsonl`), and the guarantee
that every record traces back to evidence. Everything here is open
and free to implement, forever.

**What this repo is not:** the engine. How captures become
understanding — the compilation, the cartography, the narration — is
Expand All @@ -27,12 +34,19 @@ license; running an engine does. Open doors, paid brain.

## Quick start

**In (sensor):**

1. Write a capture file (copy one from [examples/](examples/)).
2. Name it `anything.bevia-capture.json`.
3. Put it in a folder your Bevia app watches.

That's the whole integration. For a continuous sensor, see the
reference implementations.
**Out (consumer):**

1. Point your Bevia app's export at a folder.
2. Read `*.bevia-export.jsonl` — one record per line, deduped by `id`.

That's the whole integration, both directions. For a continuous
sensor, see the reference implementations.

## License

Expand Down
173 changes: 173 additions & 0 deletions SPEC-EXPORT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
# The Bevia Export Protocol — v1.1 (draft)

This document specifies how understanding leaves a Bevia engine: the
export record, the drop-file convention, and the pull convention. It
is the complete public contract for building a **consumer** — any
tool that reads what an engine has come to understand. It is the
companion to [SPEC.md](SPEC.md) (the capture protocol, v1): captures
go in through one door, records come out through this one.

**Scope.** This spec describes the engine's *exit doors*: what comes
out, and what a consumer may rely on. It does not describe the engine
— how captures become understanding is proprietary and out of scope,
and no part of this spec depends on it.

---

## 1. The export record

An export is a stream of **records** — one JSON object per exported
thing. Every record has the same envelope:

```json
{
"record": {
"id": "ter_9f2c1a",
"kind": "territory",
"label": "Hip actuator maintenance",
"summary": "Recurring inspection and repair work on the robot's hip actuator, spanning three sessions.",
"first_seen_at": "2026-07-01T08:14:00Z",
"last_seen_at": "2026-07-12T09:03:30Z",
"provenance": { "evidence_ids": ["ev_a41b", "ev_c9d0"] }
},
"envelope": { }
}
```

| Field | Required | Meaning |
|---|---|---|
| `record.id` | **yes** | Stable identity. The same thing carries the same `id` across exports, forever. |
| `record.kind` | **yes** | What kind of record this is (§2). Consumers MUST skip unknown kinds, never fail on them. |
| `record.label` | no | Short handle for pointing at the record. |
| `record.summary` | no | Narrative description, meaning-preserving. |
| `record.provenance` | per-kind | How this record traces back to evidence (§2). Required on every kind except `evidence`. |
| `record.location` | no | The location object from the capture protocol (SPEC.md §3), round-tripped unchanged. |
| `record.exported_at` | no | When the engine wrote this version of the record. Later `exported_at` for the same `id` supersedes earlier. |
| `envelope` | no | Opaque engine-side metadata, safe to ignore. Never load-bearing. |

Unknown fields are ignored by consumers (forward compatibility);
consumers MUST NOT rely on unknown fields being present.

## 2. Record kinds

Four kinds are defined in v1.1. The set is additive — future
versions add kinds, never remove or repurpose them.

| Kind | What it is | Provenance shape |
|---|---|---|
| `evidence` | A captured observation, given back — the round-trip of intake. | none (it IS the evidence) |
| `territory` | A region of recurring meaning the engine found across evidence. | `evidence_ids` |
| `connection` | A typed link between two exported records. | `evidence_ids` |
| `reading` | A rendered narrative composed from the records it cites. | `record_ids` |

**`evidence`** carries the capture fields back out, per turn:

```json
{ "record": {
"id": "ev_a41b",
"kind": "evidence",
"thread_id": "robot-g1:session-042",
"source_kind": "oneshot_session",
"speaker": "operator",
"text": "has this joint clicked before?",
"occurred_at": "2026-07-12T09:01:00Z",
"location": { "provider": "named", "domain_id": "site-14", "anchor_id": "robot-g1", "label": "Loading dock" }
} }
```

**`connection`** links two records by `id`:

```json
{ "record": {
"id": "con_77e0",
"kind": "connection",
"from_id": "ter_9f2c1a",
"to_id": "ter_b3d4e5",
"relation": "related",
"provenance": { "evidence_ids": ["ev_c9d0"] }
} }
```

`relation` is lowercase snake_case; consumers MUST tolerate relation
values they don't recognize.

**`reading`** is prose composed from other records:

```json
{ "record": {
"id": "read_2026-07-12",
"kind": "reading",
"label": "Daily read",
"body": "Maintenance work on the hip actuator continued; the clicking reported in June has not recurred since the re-torque.",
"composed_at": "2026-07-12T18:00:00Z",
"provenance": { "record_ids": ["ter_9f2c1a", "ev_a41b"] }
} }
```

## 3. The drop-file convention

A file whose name ends in **`.bevia-export.jsonl`**, written by a
Bevia app into a folder the user chose, is an export: **one record
per line**, each line exactly the record envelope of §1. The mirror
of the intake drop-file — one contract, any transport.

- **Atomic writes:** engines write to a temp name, then rename —
consumers may read on first sight of the suffix.
- **Repeat-safe reading:** re-reading any export file is always safe.
Consumers deduplicate by `record.id`; the version with the latest
`exported_at` (or, absent that, the latest-read occurrence) wins.
- **Snapshots and deltas:** a file may contain the whole map or just
what changed. The dedupe rule above makes the difference invisible
to a correct consumer.

Any tool that can read a file is a consumer. No SDK, no network:
local engine to local file to local tool.

## 4. The pull convention

Engines that expose a network exit do so **pull-shaped**: the
consumer issues an HTTP `GET` with a bearer key the user created,
and receives records as JSON or streamed JSONL. The consumer chooses
the cadence — a cron job, a poll loop, an agent's question.

- **Auth:** `Authorization: Bearer <key>`. Keys are user-created and
user-revocable; revoking a key closes the door.
- **Rate limiting:** engines may answer `429` with a `Retry-After`
header. Consumers SHOULD honor it.
- **Pull, not push:** an engine never initiates delivery to a
destination the user did not explicitly configure.

## 5. Behavioral guarantees

What a consumer may rely on, stated as behavior:

1. **Everything traces back.** Every record except `evidence` carries
provenance to the records or evidence that produced it. Nothing
comes out of the engine unattributed.
2. **Stable identity.** The same thing keeps the same `id` across
exports. Re-exporting is always safe; consumers dedupe by `id`.
3. **Event time is honored.** Time fields describe when things
happened or were observed in the user's timeline — never when the
engine processed them.
4. **No verdicts.** Records describe observed structure and carry its
evidence. They never contain scores, ratings, or judgments about a
person.
5. **The exit is never sold separately.** An engine that persists a
record makes it exportable. Export of the user's own records is
not metered or withheld behind additional payment.
6. **Location round-trips.** A capture that carried a location object
produces evidence that carries it back out, unchanged.

## 6. Versioning

This spec is versioned with the capture protocol and **additive-only**:
kinds and fields may be added; existing kinds, fields, requiredness,
and the guarantees in §5 do not change within v1.x. Breaking changes
require a v2 and a new suffix negotiation.

## 7. Out of scope

Engine behavior beyond §5; how records are computed; interpretation
of any kind; licensing of any Bevia engine. Implementing this spec —
as an engine or as a consumer — requires no license; running a Bevia
engine does.
22 changes: 11 additions & 11 deletions SPEC.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,18 @@ evidence only: sensors capture, they never interpret.
```json
{
"capture": {
"thread_id": "terri-g1:session-042",
"thread_id": "robot-g1:session-042",
"source_kind": "oneshot_session",
"source_platform": "shipmemory",
"source_platform": "example",
"source_url": "https://example.com/optional-origin",
"conversation": [
{ "speaker": "operator", "text": "has this joint clicked before?", "emitted_at": "2026-07-12T09:01:00Z" },
{ "speaker": "ai", "text": "Checking the manual…", "emitted_at": "2026-07-12T09:01:04Z" }
],
"location": {
"provider": "posemesh",
"domain_id": "store-oslo-14",
"anchor_id": "terri-g1",
"provider": "named",
"domain_id": "site-14",
"anchor_id": "robot-g1",
"label": "Loading dock"
}
},
Expand All @@ -48,7 +48,7 @@ evidence only: sensors capture, they never interpret.
| `capture.conversation[].emitted_at` | no | **Event time** (RFC 3339): when this was said/observed — not when it was exported. |
| `capture.thread_id` | no | Stable identity for the conversation/session. Same thread over time → same `thread_id`. |
| `capture.source_kind` | no | What kind of source this is (see §5 naming). Sensors SHOULD set it explicitly. |
| `capture.source_platform` | no | The producing platform/tool, e.g. `shipmemory`. |
| `capture.source_platform` | no | The producing platform/tool, e.g. `my-capture-tool`. |
| `capture.source_url` | no | Link back to the origin, if one exists. |
| `capture.location` | no | Where the knowledge happened (§3). |
| `envelope` | no | Opaque sensor-side metadata, carried through for observability. Never load-bearing. |
Expand Down Expand Up @@ -80,7 +80,7 @@ Provider-shaped so any spatial system fits without schema changes.

| Field | Meaning |
|---|---|
| `provider` | Spatial system that produced the reading: `posemesh`, `named`, `gps`, … |
| `provider` | Spatial system that produced the reading: `gps`, `named`, or any anchor-based spatial protocol. |
| `domain_id` | The space itself (e.g. one store, one ship, one site). |
| `anchor_id` | Nearest anchor/asset/portal within the space. |
| `pose` | `{x, y, z}` within the domain, in the provider's units. |
Expand Down Expand Up @@ -122,7 +122,7 @@ within v1. Breaking changes require a v2 and a new suffix negotiation.

## 7. Out of scope

Engine behavior beyond §4; interpretation of any kind; query and export
surfaces (a separate export-format spec is planned as v1.1); licensing
of any Bevia engine. Implementing this spec requires no license; running
an engine does.
Engine behavior beyond §4; interpretation of any kind; licensing of
any Bevia engine. The exit door — how records come back out — is
specified separately in [SPEC-EXPORT.md](SPEC-EXPORT.md) (v1.1).
Implementing this spec requires no license; running an engine does.
10 changes: 5 additions & 5 deletions examples/robot-session.bevia-capture.json
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
{
"capture": {
"thread_id": "terri-g1:session-042",
"thread_id": "robot-g1:session-042",
"source_kind": "oneshot_session",
"source_platform": "shipmemory",
"source_platform": "example",
"conversation": [
{ "speaker": "operator", "text": "has this joint clicked before?", "emitted_at": "2026-07-12T09:01:00Z" },
{ "speaker": "ai", "text": "Checking the manual for the hip actuator…", "emitted_at": "2026-07-12T09:01:04Z" },
{ "speaker": "tool", "text": "{\"question\":\"actuator inspection step complete\",\"response\":\"confirmed\"}", "emitted_at": "2026-07-12T09:03:30Z" }
],
"location": {
"provider": "posemesh",
"domain_id": "store-oslo-14",
"anchor_id": "terri-g1",
"provider": "named",
"domain_id": "site-14",
"anchor_id": "robot-g1",
"label": "Loading dock"
}
}
Expand Down
5 changes: 5 additions & 0 deletions examples/robot-session.bevia-export.jsonl
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{"record":{"id":"ev_a41b","kind":"evidence","thread_id":"robot-g1:session-042","source_kind":"oneshot_session","speaker":"operator","text":"has this joint clicked before?","occurred_at":"2026-07-12T09:01:00Z","location":{"provider":"named","domain_id":"site-14","anchor_id":"robot-g1","label":"Loading dock"}}}
{"record":{"id":"ev_c9d0","kind":"evidence","thread_id":"robot-g1:session-042","source_kind":"oneshot_session","speaker":"ai","text":"Checking the manual for the hip actuator…","occurred_at":"2026-07-12T09:01:04Z","location":{"provider":"named","domain_id":"site-14","anchor_id":"robot-g1","label":"Loading dock"}}}
{"record":{"id":"ter_9f2c1a","kind":"territory","label":"Hip actuator maintenance","summary":"Recurring inspection and repair work on the robot's hip actuator, spanning three sessions.","first_seen_at":"2026-07-01T08:14:00Z","last_seen_at":"2026-07-12T09:03:30Z","exported_at":"2026-07-12T18:00:00Z","provenance":{"evidence_ids":["ev_a41b","ev_c9d0"]}}}
{"record":{"id":"con_77e0","kind":"connection","from_id":"ter_9f2c1a","to_id":"ter_b3d4e5","relation":"related","provenance":{"evidence_ids":["ev_c9d0"]}}}
{"record":{"id":"read_2026-07-12","kind":"reading","label":"Daily read","body":"Maintenance work on the hip actuator continued; the clicking reported in June has not recurred since the re-torque.","composed_at":"2026-07-12T18:00:00Z","provenance":{"record_ids":["ter_9f2c1a","ev_a41b"]}}}
8 changes: 4 additions & 4 deletions reference/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@

Implementations of this protocol, usable as-is or as starting points:

- **bevia-spatial-intake** — thin intake clients: a Mentra-glasses
capture scaffold and a ShipMemory/Auki bridge poller that lands
robotics assets' ContextCards and session history as captures.
(Link published when the repo is made public.)
- **Reference intake clients** — thin sensors for spatial and
robotics capture sources: a wearable-capture scaffold and a bridge
poller that lands device session history and context as captures.
(Links published when the repos are made public.)
- **An iOS Shortcut, a shell script, a cron job** — any tool that can
write a `*.bevia-capture.json` file into a watched folder is a
complete sensor. No SDK required; that is the point of the protocol.
2 changes: 1 addition & 1 deletion schema/capture.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
},
"source_platform": {
"type": "string",
"description": "The producing platform/tool (e.g. shipmemory)."
"description": "The producing platform/tool (e.g. my-capture-tool)."
},
"source_url": {
"type": "string",
Expand Down
Loading
Loading