bigwolfe
init
2510c5e
openapi: 3.1.0
info:
title: Obsidian-Like Docs Viewer API
version: 1.0.0
description: |
Multi-tenant Obsidian-like documentation viewer with full-text search, wikilinks, and tags.
## Authentication
All endpoints require Bearer JWT authentication via the `Authorization` header (except in local mode).
## Multi-tenancy
All operations are scoped to the authenticated user's vault. Users cannot access other users' notes.
## Path Encoding
Note paths in URLs must be URL-encoded (e.g., `api/design.md` becomes `api%2Fdesign.md`).
contact:
name: API Support
license:
name: MIT
servers:
- url: http://localhost:8000
description: Local development server
- url: https://your-space.hf.space
description: Hugging Face Space deployment
security:
- BearerAuth: []
tags:
- name: Authentication
description: User authentication and token management
- name: Notes
description: CRUD operations for notes
- name: Search
description: Full-text search and navigation
- name: Index
description: Index health and rebuild operations
paths:
/api/tokens:
post:
summary: Issue JWT token
description: Issue a new JWT access token for the authenticated user (90-day expiration)
operationId: issueToken
tags:
- Authentication
security:
- BearerAuth: []
responses:
'200':
description: Token issued successfully
content:
application/json:
schema:
$ref: '#/components/schemas/TokenResponse'
example:
token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhbGljZSIsImlhdCI6MTczNjk1NjgwMCwiZXhwIjoxNzQ0NzMyODAwfQ.signature
token_type: bearer
expires_at: "2025-04-15T10:30:00Z"
'401':
$ref: '#/components/responses/Unauthorized'
'500':
$ref: '#/components/responses/InternalServerError'
/api/me:
get:
summary: Get current user info
description: Retrieve authenticated user information including HF profile
operationId: getCurrentUser
tags:
- Authentication
responses:
'200':
description: User information retrieved
content:
application/json:
schema:
$ref: '#/components/schemas/User'
example:
user_id: alice
hf_profile:
username: alice
name: Alice Smith
avatar_url: https://cdn-avatars.huggingface.co/v1/alice
vault_path: /data/vaults/alice
created: "2025-01-15T10:30:00Z"
'401':
$ref: '#/components/responses/Unauthorized'
'500':
$ref: '#/components/responses/InternalServerError'
/api/notes:
get:
summary: List notes
description: List all notes in the user's vault with optional folder filtering
operationId: listNotes
tags:
- Notes
parameters:
- name: folder
in: query
description: Filter notes by folder path (e.g., "api" or "guides/tutorials")
required: false
schema:
type: string
maxLength: 256
example: api
responses:
'200':
description: List of notes
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/NoteSummary'
example:
- note_path: api/design.md
title: API Design
updated: "2025-01-15T14:30:00Z"
- note_path: api/endpoints.md
title: API Endpoints
updated: "2025-01-14T09:15:00Z"
'401':
$ref: '#/components/responses/Unauthorized'
'500':
$ref: '#/components/responses/InternalServerError'
/api/notes/{path}:
get:
summary: Get note
description: Retrieve full note content including metadata, body, and version
operationId: getNote
tags:
- Notes
parameters:
- $ref: '#/components/parameters/NotePath'
responses:
'200':
description: Note retrieved successfully
content:
application/json:
schema:
$ref: '#/components/schemas/Note'
example:
user_id: alice
note_path: api/design.md
version: 5
title: API Design
metadata:
tags:
- backend
- api
project: auth-service
body: "# API Design\n\nThis document describes the API architecture...\n\n## Endpoints\n\nSee [[Endpoints]] for details."
created: "2025-01-10T09:00:00Z"
updated: "2025-01-15T14:30:00Z"
size_bytes: 4096
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
'404':
description: Note not found
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error: not_found
message: Note not found
detail:
note_path: api/design.md
'500':
$ref: '#/components/responses/InternalServerError'
put:
summary: Create or update note
description: Create a new note or update an existing note with optional optimistic concurrency control
operationId: createOrUpdateNote
tags:
- Notes
parameters:
- $ref: '#/components/parameters/NotePath'
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/NoteUpdate'
example:
title: API Design
metadata:
tags:
- backend
- api
project: auth-service
body: "# API Design\n\nThis document describes the API architecture..."
if_version: 4
responses:
'200':
description: Note updated successfully
content:
application/json:
schema:
$ref: '#/components/schemas/NoteResponse'
example:
status: ok
path: api/design.md
version: 5
'201':
description: Note created successfully
content:
application/json:
schema:
$ref: '#/components/schemas/NoteResponse'
example:
status: ok
path: api/design.md
version: 1
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
'409':
$ref: '#/components/responses/Conflict'
'413':
$ref: '#/components/responses/PayloadTooLarge'
'500':
$ref: '#/components/responses/InternalServerError'
delete:
summary: Delete note
description: Delete a note and remove it from all indices
operationId: deleteNote
tags:
- Notes
parameters:
- $ref: '#/components/parameters/NotePath'
responses:
'200':
description: Note deleted successfully
content:
application/json:
schema:
$ref: '#/components/schemas/DeleteResponse'
example:
status: ok
path: api/design.md
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
'404':
description: Note not found
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error: not_found
message: Note not found
detail:
note_path: api/design.md
'500':
$ref: '#/components/responses/InternalServerError'
/api/search:
get:
summary: Search notes
description: Full-text search across all notes with ranking (title 3x weight, body 1x, recency bonus)
operationId: searchNotes
tags:
- Search
parameters:
- name: q
in: query
description: Search query (tokenized, case-insensitive)
required: true
schema:
type: string
minLength: 1
maxLength: 256
example: authentication
- name: limit
in: query
description: Maximum number of results to return
required: false
schema:
type: integer
minimum: 1
maximum: 100
default: 50
responses:
'200':
description: Search results
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/SearchResult'
example:
- note_path: api/auth.md
title: Authentication Flow
snippet: "...describes the <mark>authentication</mark> process using JWT tokens..."
score: 8.5
updated: "2025-01-15T14:30:00Z"
- note_path: guides/setup.md
title: Setup Guide
snippet: "...configure <mark>authentication</mark> settings in the config file..."
score: 3.2
updated: "2025-01-10T09:00:00Z"
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'500':
$ref: '#/components/responses/InternalServerError'
/api/backlinks/{path}:
get:
summary: Get backlinks
description: Get all notes that reference the specified note via wikilinks
operationId: getBacklinks
tags:
- Search
parameters:
- name: path
in: path
description: URL-encoded note path (includes .md extension)
required: true
schema:
type: string
maxLength: 256
example: api%2Fdesign.md
responses:
'200':
description: Backlinks retrieved
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/BacklinkResult'
example:
- note_path: guides/architecture.md
title: Architecture Overview
- note_path: api/endpoints.md
title: API Endpoints
'401':
$ref: '#/components/responses/Unauthorized'
'404':
description: Note not found
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error: not_found
message: Note not found
detail:
note_path: api/design.md
'500':
$ref: '#/components/responses/InternalServerError'
/api/tags:
get:
summary: List all tags
description: Get all tags used in the user's vault with note counts
operationId: listTags
tags:
- Search
responses:
'200':
description: List of tags
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Tag'
example:
- tag: backend
count: 15
- tag: api
count: 12
- tag: frontend
count: 8
'401':
$ref: '#/components/responses/Unauthorized'
'500':
$ref: '#/components/responses/InternalServerError'
/api/index/health:
get:
summary: Get index health status
description: Retrieve index health metrics including note count and last update timestamps
operationId: getIndexHealth
tags:
- Index
responses:
'200':
description: Index health metrics
content:
application/json:
schema:
$ref: '#/components/schemas/IndexHealth'
example:
user_id: alice
note_count: 142
last_full_rebuild: "2025-01-01T00:00:00Z"
last_incremental_update: "2025-01-15T14:30:00Z"
'401':
$ref: '#/components/responses/Unauthorized'
'500':
$ref: '#/components/responses/InternalServerError'
/api/index/rebuild:
post:
summary: Trigger full index rebuild
description: Manually rebuild all indices from scratch by re-scanning vault files
operationId: rebuildIndex
tags:
- Index
responses:
'200':
description: Index rebuild completed
content:
application/json:
schema:
$ref: '#/components/schemas/RebuildResponse'
example:
status: ok
notes_indexed: 142
duration_ms: 1250
'401':
$ref: '#/components/responses/Unauthorized'
'500':
$ref: '#/components/responses/InternalServerError'
components:
securitySchemes:
BearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
description: JWT token obtained from POST /api/tokens
parameters:
NotePath:
name: path
in: path
description: URL-encoded note path (includes .md extension, e.g., "api%2Fdesign.md")
required: true
schema:
type: string
maxLength: 256
example: api%2Fdesign.md
schemas:
User:
type: object
required:
- user_id
- vault_path
- created
properties:
user_id:
type: string
minLength: 1
maxLength: 64
description: Internal user identifier
example: alice
hf_profile:
$ref: '#/components/schemas/HFProfile'
vault_path:
type: string
description: Absolute path to user's vault directory
example: /data/vaults/alice
created:
type: string
format: date-time
description: Account creation timestamp (ISO 8601)
example: "2025-01-15T10:30:00Z"
HFProfile:
type: object
properties:
username:
type: string
description: Hugging Face username
example: alice
name:
type: string
nullable: true
description: Display name from HF profile
example: Alice Smith
avatar_url:
type: string
format: uri
nullable: true
description: Profile picture URL
example: https://cdn-avatars.huggingface.co/v1/alice
NoteMetadata:
type: object
description: Arbitrary frontmatter key-value pairs (YAML)
properties:
title:
type: string
nullable: true
tags:
type: array
items:
type: string
nullable: true
project:
type: string
nullable: true
created:
type: string
format: date-time
nullable: true
updated:
type: string
format: date-time
nullable: true
additionalProperties: true
example:
tags:
- backend
- api
project: auth-service
Note:
type: object
required:
- user_id
- note_path
- version
- title
- body
- created
- updated
- size_bytes
properties:
user_id:
type: string
description: Owner user ID
example: alice
note_path:
type: string
minLength: 1
maxLength: 256
description: Relative path to vault root (includes .md)
example: api/design.md
version:
type: integer
minimum: 1
description: Optimistic concurrency version counter
example: 5
title:
type: string
minLength: 1
description: Display title (from frontmatter, H1, or filename)
example: API Design
metadata:
$ref: '#/components/schemas/NoteMetadata'
body:
type: string
description: Markdown content (excluding frontmatter)
example: "# API Design\n\nThis document describes..."
created:
type: string
format: date-time
description: Creation timestamp (ISO 8601)
example: "2025-01-10T09:00:00Z"
updated:
type: string
format: date-time
description: Last modification timestamp (ISO 8601)
example: "2025-01-15T14:30:00Z"
size_bytes:
type: integer
minimum: 0
maximum: 1048576
description: File size in bytes (max 1 MiB)
example: 4096
NoteSummary:
type: object
required:
- note_path
- title
- updated
properties:
note_path:
type: string
example: api/design.md
title:
type: string
example: API Design
updated:
type: string
format: date-time
example: "2025-01-15T14:30:00Z"
NoteUpdate:
type: object
required:
- body
properties:
title:
type: string
nullable: true
description: Override title (if not set, will be extracted from frontmatter or body)
metadata:
$ref: '#/components/schemas/NoteMetadata'
body:
type: string
maxLength: 1048576
description: Markdown content (max 1 MiB UTF-8)
if_version:
type: integer
minimum: 1
nullable: true
description: Expected version for optimistic concurrency (409 if mismatch)
example: 4
NoteResponse:
type: object
required:
- status
- path
- version
properties:
status:
type: string
enum:
- ok
path:
type: string
example: api/design.md
version:
type: integer
example: 5
DeleteResponse:
type: object
required:
- status
- path
properties:
status:
type: string
enum:
- ok
path:
type: string
example: api/design.md
SearchResult:
type: object
required:
- note_path
- title
- snippet
- score
- updated
properties:
note_path:
type: string
example: api/auth.md
title:
type: string
example: Authentication Flow
snippet:
type: string
description: Highlighted excerpt with <mark> tags
example: "...describes the <mark>authentication</mark> process using JWT..."
score:
type: number
format: float
description: Relevance score (title 3x, body 1x, recency bonus)
example: 8.5
updated:
type: string
format: date-time
example: "2025-01-15T14:30:00Z"
BacklinkResult:
type: object
required:
- note_path
- title
properties:
note_path:
type: string
example: guides/architecture.md
title:
type: string
example: Architecture Overview
Tag:
type: object
required:
- tag
- count
properties:
tag:
type: string
description: Tag name (lowercase, normalized)
example: backend
count:
type: integer
minimum: 0
description: Number of notes with this tag
example: 15
IndexHealth:
type: object
required:
- user_id
- note_count
properties:
user_id:
type: string
example: alice
note_count:
type: integer
minimum: 0
description: Total notes indexed
example: 142
last_full_rebuild:
type: string
format: date-time
nullable: true
description: Timestamp of last manual rebuild
example: "2025-01-01T00:00:00Z"
last_incremental_update:
type: string
format: date-time
nullable: true
description: Timestamp of last write/delete operation
example: "2025-01-15T14:30:00Z"
RebuildResponse:
type: object
required:
- status
- notes_indexed
- duration_ms
properties:
status:
type: string
enum:
- ok
notes_indexed:
type: integer
minimum: 0
example: 142
duration_ms:
type: integer
minimum: 0
description: Rebuild duration in milliseconds
example: 1250
TokenResponse:
type: object
required:
- token
- token_type
- expires_at
properties:
token:
type: string
description: JWT access token
example: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
token_type:
type: string
enum:
- bearer
example: bearer
expires_at:
type: string
format: date-time
description: Token expiration timestamp (90 days from issuance)
example: "2025-04-15T10:30:00Z"
Error:
type: object
required:
- error
- message
properties:
error:
type: string
description: Error code (machine-readable)
example: validation_error
message:
type: string
description: Human-readable error message
example: Invalid note path format
detail:
type: object
nullable: true
description: Additional error context
additionalProperties: true
example:
field: note_path
reason: Path must end with .md
responses:
BadRequest:
description: Bad request (invalid input)
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
examples:
invalidPath:
summary: Invalid note path
value:
error: validation_error
message: Invalid note path format
detail:
field: note_path
reason: Path must end with .md
emptyQuery:
summary: Empty search query
value:
error: validation_error
message: Search query cannot be empty
detail:
field: query
reason: Query must be at least 1 character
Unauthorized:
description: Authentication required or token invalid/expired
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
examples:
missingToken:
summary: Missing authorization header
value:
error: unauthorized
message: Authorization header required
expiredToken:
summary: Expired JWT token
value:
error: token_expired
message: Token expired, please re-authenticate
invalidToken:
summary: Invalid JWT token
value:
error: invalid_token
message: Invalid token signature
Forbidden:
description: Forbidden (vault limit exceeded or permission denied)
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
examples:
vaultLimitExceeded:
summary: Vault note limit exceeded
value:
error: vault_note_limit_exceeded
message: Vault has reached maximum of 5,000 notes
detail:
current_count: 5000
max_limit: 5000
pathTraversal:
summary: Path traversal attempt
value:
error: forbidden
message: Path escapes vault root
detail:
note_path: ../../../etc/passwd
Conflict:
description: Version conflict (optimistic concurrency failure)
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error: version_conflict
message: Note was modified since you opened it
detail:
expected_version: 4
current_version: 6
note_path: api/design.md
PayloadTooLarge:
description: Note content exceeds 1 MiB limit
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error: payload_too_large
message: Note content exceeds maximum size of 1 MiB
detail:
size_bytes: 1572864
max_size_bytes: 1048576
InternalServerError:
description: Internal server error
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error: internal_error
message: An unexpected error occurred
detail:
request_id: req_abc123