docs(vault): update skill with correct livesync two-level chunk format
This commit is contained in:
@@ -10,79 +10,103 @@ Obsidian vault backed by CouchDB (obsidian-livesync) at `obsidian.ginnoir.com`.
|
||||
## Credentials
|
||||
|
||||
```
|
||||
Endpoint : https://obsidian.ginnoir.com
|
||||
Endpoint : http://admin:couchdb_obs_p4ss_7x9k@localhost:5984 (via docker exec couchdb)
|
||||
DB : obsidian
|
||||
User : admin
|
||||
Password : couchdb_obs_p4ss_7x9k (also in stacks/notes/stack.env)
|
||||
External : https://obsidian.ginnoir.com (also in stacks/notes/stack.env)
|
||||
```
|
||||
|
||||
## Document format
|
||||
## Document format (confirmed from live vault)
|
||||
|
||||
Each note is a CouchDB document:
|
||||
obsidian-livesync uses a **two-level structure**:
|
||||
|
||||
| Field | Meaning |
|
||||
|--------|---------|
|
||||
| `_id` | Vault-relative path, e.g. `claude/Context.md` or `Projects/homelab.md` |
|
||||
| `data` | Raw markdown content |
|
||||
| `type` | `"plain"` for regular notes |
|
||||
| `ctime`/`mtime` | Unix ms timestamps |
|
||||
| `size` | Byte length of `data` |
|
||||
**File doc** — `_id` is the vault-relative path (e.g. `testing.md`, `claude/Context.md`):
|
||||
```json
|
||||
{
|
||||
"_id": "testing.md",
|
||||
"path": "testing.md",
|
||||
"type": "plain",
|
||||
"children": ["h:1w5waerza6mc9"],
|
||||
"ctime": 1780735543814,
|
||||
"mtime": 1780735679684,
|
||||
"size": 14,
|
||||
"eden": {}
|
||||
}
|
||||
```
|
||||
|
||||
Slashes in `_id` must be URL-encoded as `%2F` in requests.
|
||||
**Leaf/chunk doc** — `_id` is `h:<short-hash>`, holds the actual content:
|
||||
```json
|
||||
{
|
||||
"_id": "h:1w5waerza6mc9",
|
||||
"data": "this is a test",
|
||||
"type": "leaf"
|
||||
}
|
||||
```
|
||||
|
||||
## Read all notes
|
||||
Content lives in leaf docs. `children[]` lists chunk IDs in order (concatenate for large notes).
|
||||
Slashes in paths must be URL-encoded as `%2F` in requests.
|
||||
|
||||
## Read all notes (list)
|
||||
|
||||
```powershell
|
||||
ssh -o BatchMode=yes ginnoir@valhalla 'docker exec couchdb curl -sf "http://admin:couchdb_obs_p4ss_7x9k@localhost:5984/obsidian/_all_docs?include_docs=true"'
|
||||
```
|
||||
|
||||
Returns JSON with `rows[].doc.data` = markdown for each note.
|
||||
File docs: `type == "plain"`, `_id` does not start with `h:` or `obsydian_`.
|
||||
|
||||
## Read one note
|
||||
## Read one note's content
|
||||
|
||||
```powershell
|
||||
# e.g. claude/Context.md
|
||||
ssh -o BatchMode=yes ginnoir@valhalla 'docker exec couchdb curl -sf "http://admin:couchdb_obs_p4ss_7x9k@localhost:5984/obsidian/claude%2FContext.md"'
|
||||
# 1. Get file doc — find chunk IDs in children[]
|
||||
ssh -o BatchMode=yes ginnoir@valhalla 'docker exec couchdb curl -sf "http://admin:couchdb_obs_p4ss_7x9k@localhost:5984/obsidian/testing.md"'
|
||||
|
||||
# 2. Fetch chunk — content is in .data
|
||||
ssh -o BatchMode=yes ginnoir@valhalla 'docker exec couchdb curl -sf "http://admin:couchdb_obs_p4ss_7x9k@localhost:5984/obsidian/h%3A1w5waerza6mc9"'
|
||||
```
|
||||
|
||||
## Write / update a note
|
||||
## Write a new note
|
||||
|
||||
Shell escaping inside SSH is treacherous with JSON. The reliable pattern is:
|
||||
Write the leaf first, then the file doc. Use a fixed chunk ID so you can update it later.
|
||||
|
||||
1. Write JSON to `/tmp/note.json` on valhalla
|
||||
2. `docker cp` it into the container
|
||||
3. `curl -d @/tmp/note.json` from inside the container
|
||||
|
||||
```powershell
|
||||
# Step 1 — get current _rev
|
||||
$rev = (ssh -o BatchMode=yes ginnoir@valhalla 'docker exec couchdb curl -sf "http://admin:couchdb_obs_p4ss_7x9k@localhost:5984/obsidian/claude%2FContext.md"' | python -c "import sys,json; print(json.load(sys.stdin)['_rev'])")
|
||||
|
||||
# Step 2 — write JSON to valhalla /tmp
|
||||
$json = @{_rev=$rev; data="# My Note`n`nContent"; type="plain"; ctime=0; mtime=0; size=10} | ConvertTo-Json
|
||||
$json | ssh ginnoir@valhalla "cat > /tmp/note.json"
|
||||
|
||||
# Step 3 — copy to container and PUT
|
||||
```bash
|
||||
ssh -o BatchMode=yes ginnoir@valhalla '
|
||||
docker cp /tmp/note.json couchdb:/tmp/note.json
|
||||
docker exec couchdb curl -sf -X PUT \
|
||||
"http://admin:couchdb_obs_p4ss_7x9k@localhost:5984/obsidian/claude%2FContext.md" \
|
||||
-H "Content-Type: application/json" -d @/tmp/note.json
|
||||
B="http://admin:couchdb_obs_p4ss_7x9k@localhost:5984"
|
||||
NOW=$(date +%s%3N)
|
||||
CHUNK="h:claude_ctx_1"
|
||||
CONTENT="# My Note\n\nContent here."
|
||||
|
||||
docker exec couchdb curl -sf -X PUT "$B/obsidian/$CHUNK" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{\"data\":\"$CONTENT\",\"type\":\"leaf\"}"
|
||||
|
||||
docker exec couchdb curl -sf -X PUT "$B/obsidian/claude%2FContext.md" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{\"path\":\"claude/Context.md\",\"type\":\"plain\",\"children\":[\"$CHUNK\"],\"ctime\":$NOW,\"mtime\":$NOW,\"size\":${#CONTENT},\"eden\":{}}"
|
||||
'
|
||||
```
|
||||
|
||||
## Update an existing note
|
||||
|
||||
Fetch `_rev` for both the file doc and chunk doc, then re-PUT both with `_rev` included.
|
||||
|
||||
For complex content (markdown with quotes/newlines), write JSON to `/tmp/` on valhalla, `docker cp` into container, and `curl -d @/tmp/file.json` — avoids shell escaping.
|
||||
|
||||
## Load context at session start
|
||||
|
||||
```powershell
|
||||
ssh -o BatchMode=yes ginnoir@valhalla 'docker exec couchdb curl -sf "http://admin:couchdb_obs_p4ss_7x9k@localhost:5984/obsidian/claude%2FContext.md"' | python3 -c "import sys,json; print(json.load(sys.stdin)['data'])"
|
||||
# Get all file docs and their chunk content
|
||||
ssh -o BatchMode=yes ginnoir@valhalla 'docker exec couchdb curl -sf "http://admin:couchdb_obs_p4ss_7x9k@localhost:5984/obsidian/_all_docs?include_docs=true"' | python -c "
|
||||
import sys, json
|
||||
d = json.load(sys.stdin)
|
||||
chunks = {r['id']: r['doc']['data'] for r in d['rows'] if r['id'].startswith('h:')}
|
||||
for r in d['rows']:
|
||||
doc = r['doc']
|
||||
if doc.get('type') == 'plain' and not doc.get('deleted'):
|
||||
content = ''.join(chunks.get(c, '') for c in doc.get('children', []))
|
||||
print(f'=== {doc[\"_id\"]} ===')
|
||||
print(content)
|
||||
"
|
||||
```
|
||||
|
||||
## Key notes
|
||||
|
||||
| Path | Purpose |
|
||||
|------|---------|
|
||||
| `claude/Context.md` | Claude's persistent context: ongoing projects, preferences, session notes |
|
||||
| `Home.md` | Vault index / root |
|
||||
|
||||
## Notes on valhalla Python
|
||||
|
||||
Running `python3` on the valhalla *host* cannot resolve `localhost` (DNS issue in SSH env). Always use `docker exec couchdb curl` (CouchDB's own container) to hit the API — it resolves localhost fine internally.
|
||||
`python3` on the valhalla *host* cannot resolve `localhost` (DNS issue in SSH env). Always use `docker exec couchdb curl` — the container resolves localhost fine. On the Windows Claude Code host, use `python` (not `python3`).
|
||||
|
||||
Reference in New Issue
Block a user