# Manifest: what virta installs

A service-compris host learns what virta needs from one declarative
manifest, installed once by someone holding the `configurator` role
(tosijs-platform#5). It declares a single collection, `virta:event`: the
append-only log every board and agent folds. There is no task collection
— current task state is a view over the events, never stored.

```
{
  manifest: 1, name: 'virta', version: '<virta version>',
  collections: {
    'virta:event': {
      schema: <the event: id, taskId, at, by, kind, payload; closed>,
      immutable: true,
      envelope: { seq: true, requireAttribution: true },
      access: [
        { role: 'author', read: 'ALL', list: 'ALL', write: 'ALL' },
        { role: 'editor', read: 'ALL', list: 'ALL', write: 'ALL' },
      ],
    },
  },
  usesRoles: ['author', 'editor'],
}
```

Names are namespaced by library (`virta:` — the separator is `:`), so two
installed libraries wanting an `event` collection cannot collide, and the
manifest may only declare names in its own namespace. Installing again with
the same version and content is a no-op; a changed schema is a new version;
a new capability declaration needs a second `POST` with `approving`.

Version history: 0.1.0 closed the schema and the beta.1 host refused every
write (its envelope stamps tripped the schema, tosijs-platform#16); 0.1.1
opened it as the interim; 0.1.2 closes it again on beta.2 and opts into the
sequence and attribution; 0.1.3 makes the log immutable (the host landed
tosijs-platform#25 in 0.2.0-beta.4): a stored event is never rewritten or
deleted, so its place in the order is permanent.

What the manifest deliberately does not say:

- **No `unique: ['id']`.** The document's path *is* its id
  (`virta:event/<id>`), so an upsert cannot collide; a unique constraint
  would only add a privileged read to every write.
- **No way to rewrite or remove an event.** `immutable: true` (0.1.3): the
  client's push is an upsert commit, and a re-sent event with an identical
  body is a no-op (the retry case); a different body under a held id is
  refused (`409 immutable`, the whole batch unwritten), and so is a delete.
  Before 0.1.3 the host would have replaced a changed body and re-sequenced
  it (tosijs-platform#25); the client's rule — an event id always carries
  the same body — is now enforced on both sides. `immutable` can be added by
  upgrade, never dropped. Removing an event (a takedown) is the host owner
  acting on the datastore directly.
- **No server-stamped `by`.** `derive: [{ op: 'principal', to: 'by' }]`
  would make provenance unforgeable, which is the design's intent, but it
  would also overwrite the historical author on an imported GitHub issue.
  The right home is the envelope's author field (tosijs-platform gap
  analysis), where the caller's `by` and the token's identity can both
  live.
- **No `seq` or `by` from the caller's side of the envelope.** The host
  assigns `_seq` at commit and stamps `_by` from the credential; a caller
  cannot send either. `envelope: { seq: true }` opts the collection into the
  total order a log needs (writes serialise, about one commit a second — an
  event log is inside that); `requireAttribution: true` refuses a write the
  host cannot attribute, so `_by` is always there.
