docs/en/api/03-filesystem.md
OpenViking provides Unix-like file system operations for managing context.
OpenViking Server also exposes a minimal WebDAV adapter for resource files:
/webdav/resources
Phase 1 intentionally keeps the scope narrow:
PUT currently accepts UTF-8 text content only.OPTIONS, PROPFIND, GET, HEAD, PUT, DELETE, MKCOL, and MOVE are supported..abstract.md, .overview.md, .relations.json, and .path.ovlock are hidden from listings and cannot be accessed directly through WebDAV.Behavior notes:
write().Read L0 abstract (~100 tokens summary).
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| uri | str | Yes | - | Viking URI (must be a directory) |
Python SDK (Embedded / HTTP)
abstract = client.abstract("viking://resources/docs/")
print(f"Abstract: {abstract}")
# Output: "Documentation for the project API, covering authentication, endpoints..."
HTTP API
GET /api/v1/content/abstract?uri={uri}
curl -X GET "http://localhost:1933/api/v1/content/abstract?uri=viking://resources/docs/" \
-H "X-API-Key: your-key"
CLI
openviking abstract viking://resources/docs/
Response
{
"status": "ok",
"result": "Documentation for the project API, covering authentication, endpoints...",
"time": 0.1
}
Read L1 overview, applies to directories.
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| uri | str | Yes | - | Viking URI (must be a directory) |
Python SDK (Embedded / HTTP)
overview = client.overview("viking://resources/docs/")
print(f"Overview:\n{overview}")
HTTP API
GET /api/v1/content/overview?uri={uri}
curl -X GET "http://localhost:1933/api/v1/content/overview?uri=viking://resources/docs/" \
-H "X-API-Key: your-key"
CLI
openviking overview viking://resources/docs/
Response
{
"status": "ok",
"result": "## docs/\n\nContains API documentation and guides...",
"time": 0.1
}
Read L2 full content.
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| uri | str | Yes | - | Viking URI |
| offset | int | No | 0 | Starting line number (0-indexed) |
| limit | int | No | -1 | Number of lines to read, -1 means read to end |
Notes
read() accepts file URIs only. Passing an existing directory URI returns INVALID_ARGUMENT (400), not NOT_FOUND.resources, user, agent, and session scopes. Internal scopes such as temp and queue return INVALID_URI.Python SDK (Embedded / HTTP)
content = client.read("viking://resources/docs/api.md")
print(f"Content:\n{content}")
HTTP API
GET /api/v1/content/read?uri={uri}
curl -X GET "http://localhost:1933/api/v1/content/read?uri=viking://resources/docs/api.md" \
-H "X-API-Key: your-key"
CLI
openviking read viking://resources/docs/api.md
Response
{
"status": "ok",
"result": "# API Documentation\n\nFull content of the file...",
"time": 0.1
}
Update an existing file, or create a new one when mode="create", and automatically refresh related semantics and vectors.
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| uri | str | Yes | - | Existing file URI |
| content | str | Yes | - | New content to write |
| mode | str | No | replace | replace, append, or create |
| wait | bool | No | false | Wait for background semantic/vector refresh |
| timeout | float | No | null | Timeout in seconds when wait=true |
Notes
replace and append require the file to exist; create targets a new file and returns 409 Conflict when the path already exists. Directories are always rejected.create only accepts text-writable extensions: .md, .txt, .json, .yaml, .yml, .toml, .py, .js, .ts. Parent directories are created automatically..abstract.md, .overview.md, .relations.json.wait only controls whether the call waits for semantic/vector refresh to finish.regenerate_semantics or revectorize; write always refreshes related semantics and vectors.Python SDK (Embedded / HTTP)
result = client.write(
"viking://resources/docs/api.md",
"# Updated API\n\nFresh content.",
mode="replace",
wait=True,
)
print(result["root_uri"])
HTTP API
POST /api/v1/content/write
curl -X POST "http://localhost:1933/api/v1/content/write" \
-H "Content-Type: application/json" \
-H "X-API-Key: your-key" \
-d '{
"uri": "viking://resources/docs/api.md",
"content": "# Updated API\n\nFresh content.",
"mode": "replace",
"wait": true
}'
CLI
openviking write viking://resources/docs/api.md \
--content "# Updated API\n\nFresh content." \
--wait
Response
{
"status": "ok",
"result": {
"uri": "viking://resources/docs/api.md",
"root_uri": "viking://resources/docs",
"context_type": "resource",
"mode": "replace",
"written_bytes": 29,
"content_updated": true,
"semantic_status": "complete",
"vector_status": "complete",
"queue_status": {
"Semantic": {
"processed": 1,
"error_count": 0,
"errors": []
},
"Embedding": {
"processed": 2,
"error_count": 0,
"errors": []
}
}
}
}
List directory contents.
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| uri | str | Yes | - | Viking URI |
| simple | bool | No | False | Return only relative paths |
| recursive | bool | No | False | List all subdirectories recursively |
| output | str | No | agent | Output format: agent or original |
| abs_limit | int | No | 256 | Abstract length limit for agent output |
| show_all_hidden | bool | No | False | Include hidden files like -a |
| node_limit | int | No | 1000 | Maximum number of nodes to return |
| limit | int | No | None | Alias for node_limit |
Entry Structure
{
"name": "docs", # File/directory name
"size": 4096, # Size in bytes
"mode": 16877, # File mode
"modTime": "2024-01-01T00:00:00Z", # ISO timestamp
"isDir": True, # True if directory
"uri": "viking://resources/docs/", # Viking URI
"meta": {} # Optional metadata
}
Python SDK (Embedded / HTTP)
entries = client.ls("viking://resources/")
for entry in entries:
type_str = "dir" if entry['isDir'] else "file"
print(f"{entry['name']} - {type_str}")
HTTP API
GET /api/v1/fs/ls?uri={uri}&simple={bool}&recursive={bool}
# Basic listing
curl -X GET "http://localhost:1933/api/v1/fs/ls?uri=viking://resources/" \
-H "X-API-Key: your-key"
# Simple path list
curl -X GET "http://localhost:1933/api/v1/fs/ls?uri=viking://resources/&simple=true" \
-H "X-API-Key: your-key"
# Recursive listing
curl -X GET "http://localhost:1933/api/v1/fs/ls?uri=viking://resources/&recursive=true" \
-H "X-API-Key: your-key"
CLI
openviking ls viking://resources/ [--simple] [--recursive]
Response
{
"status": "ok",
"result": [
{
"name": "docs",
"size": 4096,
"mode": 16877,
"modTime": "2024-01-01T00:00:00Z",
"isDir": true,
"uri": "viking://resources/docs/"
}
],
"time": 0.1
}
Get directory tree structure.
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| uri | str | Yes | - | Viking URI |
| output | str | No | agent | Output format: agent or original |
| abs_limit | int | No | 256 | Abstract length limit for agent output |
| show_all_hidden | bool | No | False | Include hidden files like -a |
| node_limit | int | No | 1000 | Maximum number of nodes to return |
| limit | int | No | None | Alias for node_limit |
| level_limit | int | No | 3 | Maximum directory depth to traverse |
Python SDK (Embedded / HTTP)
entries = client.tree("viking://resources/")
for entry in entries:
type_str = "dir" if entry['isDir'] else "file"
print(f"{entry['rel_path']} - {type_str}")
HTTP API
GET /api/v1/fs/tree?uri={uri}
curl -X GET "http://localhost:1933/api/v1/fs/tree?uri=viking://resources/" \
-H "X-API-Key: your-key"
CLI
openviking tree viking://resources/my-project/
Response
{
"status": "ok",
"result": [
{
"name": "docs",
"size": 4096,
"isDir": true,
"rel_path": "docs/",
"uri": "viking://resources/docs/"
},
{
"name": "api.md",
"size": 1024,
"isDir": false,
"rel_path": "docs/api.md",
"uri": "viking://resources/docs/api.md"
}
],
"time": 0.1
}
Get file or directory status information.
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| uri | str | Yes | - | Viking URI |
Python SDK (Embedded / HTTP)
info = client.stat("viking://resources/docs/api.md")
print(f"Size: {info['size']}")
print(f"Is directory: {info['isDir']}")
HTTP API
GET /api/v1/fs/stat?uri={uri}
curl -X GET "http://localhost:1933/api/v1/fs/stat?uri=viking://resources/docs/api.md" \
-H "X-API-Key: your-key"
CLI
openviking stat viking://resources/my-project/docs/api.md
Response
{
"status": "ok",
"result": {
"name": "api.md",
"size": 1024,
"mode": 33188,
"modTime": "2024-01-01T00:00:00Z",
"isDir": false,
"uri": "viking://resources/docs/api.md"
},
"time": 0.1
}
Create a directory.
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| uri | str | Yes | - | Viking URI for the new directory |
| description | str | No | null | Initial directory description. When provided, it is written to .abstract.md and queued for L0 vectorization. |
Python SDK (Embedded / HTTP)
client.mkdir("viking://resources/new-project/")
client.mkdir("viking://resources/new-project/", description="API docs directory")
HTTP API
POST /api/v1/fs/mkdir
curl -X POST http://localhost:1933/api/v1/fs/mkdir \
-H "Content-Type: application/json" \
-H "X-API-Key: your-key" \
-d '{
"uri": "viking://resources/new-project/",
"description": "API docs directory"
}'
CLI
openviking mkdir viking://resources/new-project/
openviking mkdir viking://resources/new-project/ --description "API docs directory"
Response
{
"status": "ok",
"result": {
"uri": "viking://resources/new-project/"
},
"time": 0.1
}
Remove file or directory.
rm is idempotent: removing a valid URI that does not exist still succeeds.
Invalid URI formats, unsupported schemes, and non-public scopes return INVALID_URI.
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| uri | str | Yes | - | Viking URI to remove |
| recursive | bool | No | False | Remove directory recursively |
Python SDK (Embedded / HTTP)
# Remove single file
client.rm("viking://resources/docs/old.md")
# Remove directory recursively
client.rm("viking://resources/old-project/", recursive=True)
HTTP API
DELETE /api/v1/fs?uri={uri}&recursive={bool}
# Remove single file
curl -X DELETE "http://localhost:1933/api/v1/fs?uri=viking://resources/docs/old.md" \
-H "X-API-Key: your-key"
# Remove directory recursively
curl -X DELETE "http://localhost:1933/api/v1/fs?uri=viking://resources/old-project/&recursive=true" \
-H "X-API-Key: your-key"
CLI
openviking rm viking://resources/old.md [--recursive]
Response
{
"status": "ok",
"result": {
"uri": "viking://resources/docs/old.md"
},
"time": 0.1
}
Move file or directory.
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| from_uri | str | Yes | - | Source Viking URI |
| to_uri | str | Yes | - | Destination Viking URI |
Python SDK (Embedded / HTTP)
client.mv(
"viking://resources/old-name/",
"viking://resources/new-name/"
)
HTTP API
POST /api/v1/fs/mv
curl -X POST http://localhost:1933/api/v1/fs/mv \
-H "Content-Type: application/json" \
-H "X-API-Key: your-key" \
-d '{
"from_uri": "viking://resources/old-name/",
"to_uri": "viking://resources/new-name/"
}'
CLI
openviking mv viking://resources/old-name/ viking://resources/new-name/
Response
{
"status": "ok",
"result": {
"from": "viking://resources/old-name/",
"to": "viking://resources/new-name/"
},
"time": 0.1
}
Search content by pattern.
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| uri | str | Yes | - | Viking URI to search in |
| pattern | str | Yes | - | Search pattern (regex) |
| case_insensitive | bool | No | False | Ignore case |
| exclude_uri | str | No | None | URI prefix to exclude from search |
| node_limit | int | No | None | Maximum number of nodes to search |
| level_limit | int | No | 5 | Maximum directory depth to traverse |
Python SDK (Embedded / HTTP)
results = client.grep(
"viking://resources/",
"authentication",
case_insensitive=True
)
print(f"Found {results['count']} matches")
for match in results['matches']:
print(f" {match['uri']}:{match['line']}")
print(f" {match['content']}")
HTTP API
POST /api/v1/search/grep
curl -X POST http://localhost:1933/api/v1/search/grep \
-H "Content-Type: application/json" \
-H "X-API-Key: your-key" \
-d '{
"uri": "viking://resources/",
"pattern": "authentication",
"case_insensitive": true
}'
CLI
openviking grep viking://resources/ "authentication" [--ignore-case]
Response
{
"status": "ok",
"result": {
"matches": [
{
"uri": "viking://resources/docs/auth.md",
"line": 15,
"content": "User authentication is handled by..."
}
],
"count": 1
},
"time": 0.1
}
Match files by pattern.
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| pattern | str | Yes | - | Glob pattern (e.g., **/*.md) |
| uri | str | No | "viking://" | Starting URI |
| node_limit | int | No | None | Maximum number of matches to return |
Python SDK (Embedded / HTTP)
# Find all markdown files
results = client.glob("**/*.md", "viking://resources/")
print(f"Found {results['count']} markdown files:")
for uri in results['matches']:
print(f" {uri}")
# Find all Python files
results = client.glob("**/*.py", "viking://resources/")
print(f"Found {results['count']} Python files")
HTTP API
POST /api/v1/search/glob
curl -X POST http://localhost:1933/api/v1/search/glob \
-H "Content-Type: application/json" \
-H "X-API-Key: your-key" \
-d '{
"pattern": "**/*.md",
"uri": "viking://resources/"
}'
CLI
openviking glob "**/*.md" [--uri viking://resources/]
Response
{
"status": "ok",
"result": {
"matches": [
"viking://resources/docs/api.md",
"viking://resources/docs/guide.md"
],
"count": 2
},
"time": 0.1
}
Create relations between resources.
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| from_uri | str | Yes | - | Source URI |
| to_uris | str or List[str] | Yes | - | Target URI(s) |
| reason | str | No | "" | Reason for the link |
Python SDK (Embedded / HTTP)
# Single link
client.link(
"viking://resources/docs/auth/",
"viking://resources/docs/security/",
reason="Security best practices for authentication"
)
# Multiple links
client.link(
"viking://resources/docs/api/",
[
"viking://resources/docs/auth/",
"viking://resources/docs/errors/"
],
reason="Related documentation"
)
HTTP API
POST /api/v1/relations/link
# Single link
curl -X POST http://localhost:1933/api/v1/relations/link \
-H "Content-Type: application/json" \
-H "X-API-Key: your-key" \
-d '{
"from_uri": "viking://resources/docs/auth/",
"to_uris": "viking://resources/docs/security/",
"reason": "Security best practices for authentication"
}'
# Multiple links
curl -X POST http://localhost:1933/api/v1/relations/link \
-H "Content-Type: application/json" \
-H "X-API-Key: your-key" \
-d '{
"from_uri": "viking://resources/docs/api/",
"to_uris": ["viking://resources/docs/auth/", "viking://resources/docs/errors/"],
"reason": "Related documentation"
}'
CLI
openviking link viking://resources/docs/auth/ viking://resources/docs/security/ --reason "Security best practices"
Response
{
"status": "ok",
"result": {
"from": "viking://resources/docs/auth/",
"to": "viking://resources/docs/security/"
},
"time": 0.1
}
Get relations for a resource.
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| uri | str | Yes | - | Viking URI |
Python SDK (Embedded / HTTP)
relations = client.relations("viking://resources/docs/auth/")
for rel in relations:
print(f"Related: {rel['uri']}")
print(f" Reason: {rel['reason']}")
HTTP API
GET /api/v1/relations?uri={uri}
curl -X GET "http://localhost:1933/api/v1/relations?uri=viking://resources/docs/auth/" \
-H "X-API-Key: your-key"
CLI
openviking relations viking://resources/docs/auth/
Response
{
"status": "ok",
"result": [
{"uri": "viking://resources/docs/security/", "reason": "Security best practices"},
{"uri": "viking://resources/docs/errors/", "reason": "Error handling"}
],
"time": 0.1
}
Remove a relation.
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| from_uri | str | Yes | - | Source URI |
| to_uri | str | Yes | - | Target URI to unlink |
Python SDK (Embedded / HTTP)
client.unlink(
"viking://resources/docs/auth/",
"viking://resources/docs/security/"
)
HTTP API
DELETE /api/v1/relations/link
curl -X DELETE http://localhost:1933/api/v1/relations/link \
-H "Content-Type: application/json" \
-H "X-API-Key: your-key" \
-d '{
"from_uri": "viking://resources/docs/auth/",
"to_uri": "viking://resources/docs/security/"
}'
CLI
openviking unlink viking://resources/docs/auth/ viking://resources/docs/security/
Response
{
"status": "ok",
"result": {
"from": "viking://resources/docs/auth/",
"to": "viking://resources/docs/security/"
},
"time": 0.1
}
Export a resource tree as a .ovpack file.
Packages all resources under the specified URI into a .ovpack file for backup or migration. Requires ROOT or ADMIN permissions.
Processing Flow:
.ovpack)Code Entry Points:
openviking/server/routers/pack.py:export_ovpack - HTTP routeropenviking/service/pack_service.py - Core service implementationcrates/ov_cli/src/handlers.rs:handle_export - CLI handlerParameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| uri | string | Yes | - | Viking URI to export |
Permission Requirements: ROOT or ADMIN
HTTP API
POST /api/v1/pack/export
Content-Type: application/json
curl -X POST http://localhost:1933/api/v1/pack/export \
-H "Content-Type: application/json" \
-H "X-API-Key: your-admin-key" \
-d '{
"uri": "viking://resources/my-project/"
}' \
--output my-project.ovpack
Python SDK
import openviking as ov
client = ov.SyncHTTPClient(url="http://localhost:1933", api_key="your-admin-key")
client.initialize()
# Export to local file (HTTP SDK automatically handles download)
# Note: Export functionality is primarily used via CLI
CLI
# Export resource
ov export viking://resources/my-project/ ./exports/my-project.ovpack
Response Example
This endpoint directly returns a file stream (Content-Type: application/zip), does not return a JSON envelope.
Import a .ovpack file.
Imports a .ovpack file to a specified location for restoring or migrating data. Requires ROOT or ADMIN permissions.
Processing Flow:
.ovpack fileCode Entry Points:
openviking/server/routers/pack.py:import_ovpack - HTTP routeropenviking/service/pack_service.py - Core service implementationcrates/ov_cli/src/handlers.rs:handle_import - CLI handlerParameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| temp_file_id | string | Yes | - | Temporary upload file ID (obtained via temp_upload) |
| parent | string | Yes | - | Target parent URI (import to this location) |
| force | bool | No | False | Whether to overwrite existing resources |
| vectorize | bool | No | True | Whether to trigger vectorization |
Permission Requirements: ROOT or ADMIN
HTTP API
POST /api/v1/pack/import
Content-Type: application/json
# Step 1: Upload .ovpack file
TEMP_FILE_ID=$(
curl -s -X POST http://localhost:1933/api/v1/resources/temp_upload \
-H "X-API-Key: your-admin-key" \
-F "file=@./exports/my-project.ovpack" \
| jq -r '.result.temp_file_id'
)
# Step 2: Import
curl -X POST http://localhost:1933/api/v1/pack/import \
-H "Content-Type: application/json" \
-H "X-API-Key: your-admin-key" \
-d "{
\"temp_file_id\": \"$TEMP_FILE_ID\",
\"parent\": \"viking://resources/imported/\",
\"force\": true,
\"vectorize\": true
}"
Python SDK
import openviking as ov
client = ov.SyncHTTPClient(url="http://localhost:1933", api_key="your-admin-key")
client.initialize()
# Import .ovpack file (HTTP SDK automatically handles upload)
# Note: Import functionality is primarily used via CLI
CLI
# Import .ovpack file
ov import ./exports/my-project.ovpack viking://resources/imported/
# Force overwrite existing content
ov import ./exports/my-project.ovpack viking://resources/imported/ --force
# Skip vectorization
ov import ./exports/my-project.ovpack viking://resources/imported/ --no-vectorize
Response Example
{
"status": "ok",
"result": {
"uri": "viking://resources/imported/my-project/"
},
"telemetry": {
"operation_id": "550e8400-e29b-41d4-a716-446655440000"
}
}