---
name: silverbullet-api
description: Accès à l'API HTTP /.fs de SilverBullet (sb.datagrandest.net) : lire, lister, écrire des fichiers via curl + token Bearer sbt_ (le MCP silverbullet-ai est couvert par le skill silverbullet-mcp)
---

# SilverBullet — accès aux fichiers d'un espace

SilverBullet est un éditeur de notes Markdown « wiki personnel ». L'URL affichée
dans le navigateur renvoie **toujours le shell HTML du SPA**, jamais le contenu.
La vraie API est `/.fs/`, montée **à l'intérieur de l'espace**.

```
https://sb.datagrandest.net/<espace>/.fs/<chemin>
```

## Éléments clés

| Élément | Valeur |
|---|---|
| Base | `https://sb.datagrandest.net` |
| Espace | `/shizendo/` |
| Auth | `Authorization: Bearer sbt_...` |
| Listing | `GET /shizendo/.fs/` → JSON (≈155 Ko, 961 fichiers) |
| Lecture | `GET /shizendo/.fs/<chemin>` → texte brut |
| Écriture | `PUT` + **corps brut en clair** |
| Méthodes | `GET, HEAD, PUT, DELETE, POST` |

## Lecture

**Le point de l'extension doit être encodé `%2E`**, sinon le SPA intercepte la
requête et renvoie le shell HTML (2591 octets d'`<!doctype html>`) au lieu du fichier.

```bash
# Token depuis le coffre pass (source de vérité) — jamais en clair dans la conversation
T="$(~/.opencode/bin/sb-token.sh)"   # repli : cat ~/.silverbullet-token
curl -H "Authorization: Bearer $T" \
  "https://sb.datagrandest.net/shizendo/.fs/00_INBOX/test-opencode%2Emd"
```

Le chemin utilise l'encodage de SilverBullet : `encodeURIComponent` avec `/`
préservé — donc les sous-répertoires gardent leurs slashes, seul le `.` est à encoder.

## Listing récursif

```bash
curl -H "Authorization: Bearer $T" \
  "https://sb.datagrandest.net/shizendo/.fs/"
```

Réponse : **tableau JSON** (pas un objet), un fichier par entrée :

```json
{
  "name": "00_INBOX/test-opencode.md",
  "created": 1785444690006,
  "lastModified": 1785444690006,
  "contentType": "text/markdown",
  "size": 275,
  "perm": "rw"
}
```

`name` est le chemin relatif complet. Attention : **`GET .../.fs/<répertoire>/`
renvoie 404** — il n'y a pas de listing par sous-dossier, seulement le listing
racine complet, à filtrer côté client.

## Méthodes HTTP

| Requête | Résultat |
|---|---|
| `GET /shizendo/00_INBOX/...` | 302 → `.auth` (SSO), ou HTML du SPA |
| `GET /shizendo/.fs/...%2Emd` | **200, texte brut** ✅ |
| `POST` JSON `{"content":...}` | **400 `Malformed JSON body`** |
| `PUT` corps brut | **200 `OK` — écrase le fichier** |

**Lire → `GET`. Écrire → `PUT` avec le corps brut en clair**, pas de JSON :

```bash
# ❌ échoue : 400 Malformed JSON body
curl -X POST -H "Content-Type: application/json" -d '{"content":"..."}' "$URL"

# ❌ POST n'est PAS la création : c'est l'endpoint "reconcile" de synchronisation
curl -X POST -d '...' "$URL"        # → 400 Malformed JSON body

# ✅ écriture
curl -X PUT -H "Authorization: Bearer $T" \
     -H "Content-Type: text/markdown" \
     --data-binary @fichier.md "$URL"
```

Le `POST` est réservé à la **réconciliation de synchronisation** SilverBullet
(payload JSON maison, construit côté client) — ce n'est ni de la lecture ni de la
création de page. Ne pas l'utiliser.

## En-têtes utiles

`GET` renvoie des métadonnées en headers (pas seulement en `Content-Type`) :

| Header | Signification |
|---|---|
| `Content-Type` | `text/markdown`, `application/octet-stream`, … |
| `X-Content-Length` | taille réelle (≠ `Content-Length` si `X-Get-Meta: true`) |
| `X-Created` / `X-Last-Modified` | timestamps epoch ms |
| `X-Perm` | `rw` ou `ro` |
| `ETag` | `"sha256:..."` — à réutiliser en `If-Match` pour un PUT conditionnel |
| `X-Space-Path` | chemin serveur réel de l'espace |

Avec `X-Get-Meta: true`, le corps est vide (`Content-Length: 0`) : on ne récupère
que les métadonnées.

## Écriture conditionnelle (éviter d'écraser)

```bash
# If-Match → 412 si le fichier a changé entre-temps
curl -X PUT -H "Authorization: Bearer $T" \
     -H "If-Match: \"sha256:...\"" \
     --data-binary @nouveau.md "$URL"
```

`If-None-Match: *` = n'écrire que si le fichier n'existe pas encore.

## ⚠️ Règle de sécurité — ne jamais tester une écriture sur un fichier réel

Le `PUT` **écrase sans confirmation**. Une requête de test sur un chemin existant
détruit le contenu irrémédiablement (sauf si une copie a été lue avant).

**Toujours tester sur un fichier jetable** que l'on crée soi-même :

```bash
SAFE="$BASE/shizendo/.fs/Sandbox/__probe__.md"
# créer, tester PUT/DELETE, puis supprimer
curl -X DELETE -H "Authorization: Bearer $T" "$SAFE"
```

Avant toute écriture : `GET` d'abord, et conserver la réponse pour pouvoir
restaurer.

## Erreurs

| Code | Cause |
|---|---|
| `401 Unauthorized` | token absent, mal formé ou révoqué |
| `404 page not found` | chemin inexistant, **ou** listing d'un sous-dossier |
| `400 Malformed JSON body` | `POST` avec un payload non attendu |
| `405` | méthode non autorisée (`OPTIONS` → `Allow: GET,HEAD,PUT,DELETE,POST`) |
| `302 → .auth` | requête sur l'URL de page sans token (SSO YunoHost) |

## Autres endpoints

| Endpoint | Rôle |
|---|---|
| `/.fs/` (racine serveur) | `404 No space here` — doit être préfixé par l'espace |
| `/.dashboard` | Dashboard SilverBullet (HTML) |
| `/.revisions/<chemin>` | Historique : JSON `?` ou diff `?format=diff` |
| `/.revisions/<chemin>?format=diff` | Unified diff git |

```bash
# Historique d'un fichier
curl -H "Authorization: Bearer $T" \
  "https://sb.datagrandest.net/shizendo/.revisions/00_INBOX/test-opencode.md"
```

## Environnement

- **Instance** : SilverBullet « Kamio (SB - DGE) », derrière le SSO YunoHost
  (`x-sso-wat: You've just been SSOed`)
- **Espaces** : montés sous `/<nom-espace>/` (ici `shizendo`)
- **Stockage** : `/home/yunohost.app/silverbullet/notes/shizendo`
- **Token** : préfixe `sbt_`, 64 hex — à passer en `Authorization: Bearer`, jamais
  en query string. **Source de vérité : coffre pass `opencode/sb-token`**
  (récupération : `~/.opencode/bin/sb-token.sh`). Ne jamais afficher le token en clair
  dans une conversation — injecter via la substitution shell.


## Requêtes / recensement des attributs

Pour lister ou recenser des attributs de frontmatter (ex. `projects:`), lire les fichiers via `GET /.fs/` puis filtrer le frontmatter (premières lignes entre `---`) — méthode utilisée pour la migration `project:` → `projects:` (recensement, transformation ciblée de la ligne, PUT avec `If-Match`/ETag, sauvegarde locale avant écriture).

Pour exécuter des requêtes SLIQ dans l'espace, voir le skill `silverbullet-mcp` (section « Requêtes SLIQ / Lua (eval_lua) ») et la doc : https://docs.silverbullet.md/Space%20Lua/Integrated%20Query
