# lore

breadcrumbs an agent leaves while working with you: a source worth returning
to, a decision and its reason, a preference. each carries the link that makes
it usable. they are records in a [space](https://atproto.com/blog/atproto-spaces-alpha)
on your PDS, one space per project, shared by handle, readable by any client
that can read the space.

**live:** [lore.waow.tech](https://lore.waow.tech) lists your spaces and
what is in them. [llms.txt](https://lore.waow.tech/llms.txt).
**package:** [@waow.tech/lore](https://www.npmjs.com/package/@waow.tech/lore).

## use

```
pi install npm:@waow.tech/lore
/lore login you.example.com
```

once per account: a browser tab opens on your PDS, you approve the lore
permission set, and the token that comes back is scoped to it. sessions
persist under the agent dir and refresh on their own. then, in a pi session
inside a project:

| | |
| --- | --- |
| `/lore` | this project's space: owner, members, a few lines, a link to it on pds.ls. `all`, `mine` |
| `/lore note <text>` | your text, exactly as typed |
| `/lore use <key>` | use a space you name for this project instead of its own |
| `/lore share <handle>` | let someone read it and add to it; `unshare` takes it back, `members` lists them |
| `/lore join <owner> <key>` | use a space someone shared with you; `leave` stops using it here |
| `/lore as` | switch account; `/lore logout` revokes one; `/lore help` for the rest |

the agent's own breadcrumbs have no command: ask it in plain words to
remember something and it drafts one from context, with its link; you
confirm before it is written.

typing `/lore ` opens a picker of subcommands; handles complete from
[typeahead](https://typeahead.waow.tech) wherever one is typed, accounts
from the ones you have logged into. the footer shows
`● lore key · n · account` for your own space, `◇ lore key ← owner` for a
shared one, `○ lore · unbound` for none.

sharing is two acts, as the protocol makes it: the owner adds a handle, and
tells the member the key. the member joins from any project.

the space is read at session start and rendered into the system prompt as
one block: the whole space, a few lines each with its link, nothing
retrieved on demand. the block stays byte-identical for the session, so the
model's cached prefix survives a save; your own saves are not re-rendered,
their tool results already carry them. `/lore` reads the space again, and
anything new arrives as a message on the next turn. a new account or space
takes a new block.

## design

- **a project is the git repo pi was started in**, identified by its remote
  and reduced to the repo's DID on tangled so every clone agrees. a folder
  with no repo gets no space of its own, since its path is nobody else's;
  name one with `use`. a key you name is a slug.
- **the space is the unit of sharing.** memberList policy, you are the
  authority, `share` exists from day one. each participant writes under their
  own repo in the space; a reader sees the union, or `mine`.
- **one text field per record.** no structure inside it. a refinement
  rewrites the record in place, so a raw view of the space (pds.ls) shows
  what readers show, one record per breadcrumb.
- **writes are deliberate.** the tool fires only on an explicit ask and every
  save is confirmed first. no extractor, no background pipeline.
- **sign-in is OAuth, not a password.** the extension is a native client
  (`lore.waow.tech/pi/oauth-client-metadata.json`) with loopback redirects;
  the site is the web client. the same permission set scopes both, so a grant
  can be revoked per client from your PDS. an app password in the environment
  remains only for headless runs.
- **which spaces you use is not public.** the project → space registry lives
  in a private space of your own (key `lore-registry`), read by your session
  alone. the protocol has no "spaces i was added to"; habitat crawls
  `listSpaces` per session and roomy asks its own server, and neither
  publishes a pointer.
- **no bluesky in the path.** identity through slingshot, actor search and
  profile cards through typeahead, avatars from the person's own PDS.

## on the network

- `tech.waow.lore.space` — the space type; skey is the sha256 of the key
- `tech.waow.lore.entry` — `text`, `createdAt`, under the author's repo
- `tech.waow.lore.registry` — project → space, in the private registry space
- `tech.waow.lore.permissions` — the permission set the site signs in with

published from waow.tech; they resolve from `_lexicon.lore.waow.tech`.

## what spaces forces

- everyone involved needs an account on a PDS that implements the spaces
  October 1 alpha contract: the alpha host, zds instances such as pds.zat.dev, or a reference
  build from the permissioned-data branch. a member's own server issues the
  delegation token, so an account elsewhere cannot join, and the join says so.
- own-repo reads and all writes ride the session. cross-repo reads go
  through a short-lived space credential bound to a signing key, held in lore
  only. HTTP signatures bind each read to its repo or authority DID. OAuth
  sessions still use DPoP.
- the authority is always admitted; whether the host lists it as a member
  differs (zds does, the reference does not). `members` means people added.
- only the authority can remove a member. `listSpaces` includes spaces you
  hold a repo in; the site filters its own-space list by authority and uses
  your registry for joined spaces. `leave` drops your own binding, not your
  membership. invitations still arrive through the keys people give you.
- a space is anchored on your DID and cannot change hands without it. if a
  project's lore outgrows one person, the move is mechanical because entries
  were never in the space's own repo: a space on a transferable DID, members
  re-added, each registry re-pointed.

## develop

```sh
npm ci && npm ci --prefix site              # extension and website dependencies
npm run check                                # typecheck, anti-slop lint, tests
source live/env.sh && npm run test:live      # lore inside pi sessions against real PDSes, model scripted
npm run live:pi owner k "/lore use k" "hi"   # a real `pi -p` run as a throwaway account, cheap model
npm pack -w @waow.tech/lore --dry-run        # what a publish would ship
npm publish -w @waow.tech/lore --otp=<code>  # after bumping version in extensions/lore/package.json
cd site && npm run build && npm run deploy   # lore.waow.tech
```

the live suite acts as two throwaway accounts on different host
implementations (`live/env.sh` names them and pulls their app passwords
from the sops store): the owner creates a space keyed `lore-live-<time>`,
shares it, the member joins from a pi session on the other host, and the
space is deleted afterwards. the model is pi's faux provider, so every
system prompt and message the extension produces is asserted on, each phase
has a deadline and reports its wall time, and no terminal is involved.
the website client is also exercised against both hosts, including 101-record
pagination, account-separated credentials, revocation recovery, and sharing.
`live/pi.ts` drives a real `pi -p` process with a cheap model instead, for
the cases only the real agent loop reproduces.

## docs

| | |
| --- | --- |
| [changelog](CHANGELOG.md) | what changed per version, written for whoever hit the bug |
| [discovery](docs/discovery.md) | what a host tells a member about their spaces, and what an inbox for invitations would take |

## later, if wanted

background writes off the request path; a trust ladder in the prompt; search
over spaces; a path or tag within a space for finer scoping.
