benjivers/notes Public Public: anyone can read this; nobody can change it.

Files 1060c95de59213030a45f268d791921201715cde
115 lines · 6.9 KB · ~1.8k tok Raw History
1
# notes
2
3
A personal linked-notes vault, in the spirit of Obsidian — markdown,
4
`[[wikilinks]]`, backlinks, tags and a graph — kept on the forge.
5
6
## Using it
7
8
Open the app and sign in with your forge account. Your notes are **yours
9
alone**: the contract gives every verb on a note to its author, so nobody
10
else who can open the app — admins included — can list, read or change them.
11
12
- **Write** in markdown. It saves as you type (and with ⌘S). Edit, Split
13
  (side by side) and Read views; ⌘E flips between reading and writing.
14
- **Link** with `[[Another note]]`, `[[Note|shown text]]` or
15
  `[[Note#Heading]]`. Typing `[[` suggests titles. A link to a note not
16
  written yet is drawn dashed; following it creates the note.
17
- **Backlinks**: the right panel shows every note that links here, with the
18
  line it said it in, plus links out, the outline, tags and a local graph.
19
- **Rename** a note and every link to it in your vault is rewritten.
20
- **Tags**: `#tag`, nested as `#area/topic`. Click one to see every note
21
  carrying it (`#area` also finds `#area/topic`).
22
- **Search**: words, `"a phrase"`, `#tag` or `tag:name`, combined.
23
- **Graph**: every note and link, force-laid-out. Drag, zoom, click to open.
24
- **Daily note**: the calendar button (⌘⇧D) opens today's `YYYY-MM-DD` note.
25
- **Quick switcher**: ⌘K / Ctrl K — jump to a note, or ⇧↵ to create one.
26
- Editor keys: ⌘B bold, ⌘I italic, ⌘⇧H highlight, ⌘↵ cycles a line
27
  through task / done / plain, Enter continues a list, Tab indents it.
28
- Markdown: headings, emphasis, `==highlight==`, `~~strike~~`, code,
29
  fenced blocks, lists, `- [ ]` tasks (tick them in the reading view),
30
  quotes, Obsidian callouts (`> [!tip] Title`), tables, links.
31
- **Your data**: every note downloads as `.md`; the sidebar's download
32
  button exports the whole vault as JSON.
33
34
Everything is real: notes live in SQLite under `FORGE_APP_DATA`. A preview
35
of a pull gets a copy of production data; production is the default branch.
36
37
## Claude can use it too
38
39
`POST /mcp` is an MCP server with `list_notes`, `search_notes`,
40
`read_note`, `create_note`, `update_note`, `append_to_note` (with no
41
title: today's daily note) and `delete_note` — as whoever the forge grant
42
belongs to:
43
44
```sh
45
claude mcp add --transport http notes https://notes--forge-apps.benjivers.com/mcp
46
```
47
48
The terminal (`FORGE_APP_SSH`) lists, reads and searches the vault, adds a
49
line to today's note (`a`) and makes new notes (`n`).
50
51
## Running it here
52
53
```sh
54
PORT=8080 FORGE_APP_DATA=./data bun run src/server.ts   # signed out without FORGE_APP_IDENTITY_KEY
55
bun test                                                # what forge.json's app.test runs on every push
56
FORGE_APP_DATA=./data bun run src/tty.ts                # the terminal
57
bun run lint:design                                     # no colour but the tokens'
58
bun run contract:emit                                   # rewrite contract/openapi.json from the routes
59
```
60
61
## The shape
62
63
One implementation, many doors. `src/actions.ts` is every operation as a
64
plain function — `(db, who, input)` in, data or a named failure out — and
65
the page, the JSON API, the MCP server and the terminal all call it and
66
decide nothing themselves. `AGENTS.md` says how to add to it.
67
68
| file                               | what it is                                                     |
69
| ---------------------------------- | -------------------------------------------------------------- |
70
| `src/actions.ts`                   | notes: list, add, rename (relinks), pin, delete, search; audit |
71
| `src/vault.ts`                     | links, backlinks, tags, graph, search — pure, over notes       |
72
| `static/md.js`                     | the markdown renderer, shared by the server and the browser    |
73
| `contract.json`                    | who may do what: roles, and the least role each verb needs     |
74
| `src/contract.ts`, `src/can.ts`    | the table, read at boot; the forge's evaluator, verbatim       |
75
| `src/security.ts`                  | the door: rationing, same origin, the table before the body    |
76
| `src/audit.ts`                     | every change and every refused one, as a row                   |
77
| `src/who.ts`                       | who is asking, from the forge's signed headers                 |
78
| `src/db.ts`                        | SQLite under `FORGE_APP_DATA`, WAL, forward-only migrations    |
79
| `src/live.ts`                      | the in-process bus and `GET /events`                           |
80
| `src/mcp.ts`                       | the actions as MCP tools at `POST /mcp`                        |
81
| `src/routes.ts`                    | every path, in one table, each API route described beside it   |
82
| `src/openapi.ts`                   | the routes as OpenAPI; committed at `contract/openapi.json`    |
83
| `src/page.ts`                      | the HTML, rendered on the server, with its share metadata      |
84
| `design/tokens.json`, `src/tokens.ts` | the look as data, light and dark; compiled to `/tokens.css` |
85
| `src/http.ts`                      | the floor: headers, body cap, files                            |
86
| `src/server.ts`                    | boot: open the database, serve                                 |
87
| `src/tty.ts`                       | the terminal, over the same actions                            |
88
| `src/env.ts`                       | what the forge told this process                               |
89
| `static/app.css`, `static/live.js` | the look in the tokens' words, and the panels that stay current |
90
| `static/app.js`, `static/graph.js` | the editor, switcher and keys; the force-directed graph        |
91
92
## What every app stands on
93
94
- **The door.** Before a route sees a request, `src/security.ts` rations
95
  writes (and a stranger's API calls), refuses a write from another site,
96
  and turns away — before the body is read — anyone the table lets through
97
  a resource's door for no row at all: 401 for a stranger, 403 for anyone
98
  else. The action still decides about the real row.
99
- **The audit trail.** Every change, and every change refused, is a row:
100
  who, which resource and verb, which row, when, and whether they were
101
  let. Admins read it at `GET /api/audit`. A row that cannot be written
102
  is a log line, never a failed request.
103
- **The API, described.** `GET /openapi.json` is `contract/openapi.json`,
104
  written from the routes by `bun run contract:emit`; each operation names
105
  its cell of the table (`x-contract`). `bun test` fails while it is stale.
106
- **Up, or not.** `GET /healthz` answers `{ ok, version, uptime }`; the
107
  forge waits on it before it sends anyone to a deploy.
108
- **The look.** `design/tokens.json` holds every colour, space and size,
109
  light and dark; nothing else spells out a colour (`bun run lint:design`),
110
  and every pair a person reads meets 4.5:1 in both themes.
111
- **The open web.** A shared link unfurls (description, Open Graph, a
112
  card); `/robots.txt` keeps crawlers to the page, and out of previews
113
  entirely; `/.well-known/security.txt` sends a reporter to the forge
114
  that runs the app.
115
↑↓ navigate ↵ open esc close
↑↓ navigate ↵ open esc close

Keyboard shortcuts