Runtime API

Files

The file system endpoints let you list, read, write, and delete files inside the workspace VM. All paths are absolute filesystem paths (e.g. /app/src/main.go).

Requires the internal server to be enabled. All requests require a valid token - see Connection & Auth.

List directory

List files and directories in a given path. Supports recursive traversal, content inclusion, hash computation, and filtering.

const rt = await client.workspaces.runtime('ws_a1b2c3d4');

const result = await rt.files.list({
  dirPath: '/app/src',
  nested: true,
  flatten: true,
  includeContent: true,
  codeFilesOnly: true,
  maxDepth: 5,
});

console.log(result.entries);       // fileEntry[]
console.log(result.count);         // number of entries
GET https://workspace.oblien.com/files?path=/app/src&nested=true&flatten=true&include_content=true&code_files_only=true&max_depth=5
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
curl "https://workspace.oblien.com/files?path=/app/src&nested=true&flatten=true&include_content=true&code_files_only=true&max_depth=5" \
  -H "Authorization: Bearer $GATEWAY_JWT"

Parameters

ParameterTypeRequiredDescription
pathstringNoDirectory path to list. Defaults to /
nestedbooleanNoRecurse into subdirectories. Default false
flattenbooleanNoReturn flat list instead of tree. Default false
lightbooleanNoOmit size and modified time for faster response. Default false
include_hashbooleanNoInclude SHA-256 hash for each file. Default false
include_contentbooleanNoInclude file content inline. Default false
include_extensionsbooleanNoInclude file extension field. Default false
code_files_onlybooleanNoOnly return code/config files. Default false
use_gitignorebooleanNoRespect .gitignore rules. Default true
max_depthintegerNoMaximum recursion depth. Default 20
path_filterstringNoCase-insensitive substring filter on path
include_extstringNoComma-separated extensions to include (e.g. js,ts,go)
ignore_patternsstringNoComma-separated glob patterns to ignore
max_content_budgetintegerNoMax total bytes for inline content. Default ~50 MiB

Response

{
  "success": true,
  "path": "/app/src",
  "entries": [
    {
      "name": "main.go",
      "path": "/app/src/main.go",
      "type": "file",
      "size": 1234,
      "modified": "2025-01-15T10:30:00Z",
      "extension": ".go",
      "content": "package main\n...",
      "hash": "a1b2c3..."
    },
    {
      "name": "utils",
      "path": "/app/src/utils",
      "type": "directory",
      "children": [...]
    }
  ],
  "count": 42
}

The list endpoint is capped at 50,000 entries. For large directories, use path_filter, include_ext, or code_files_only to narrow results, or use the stream endpoint for NDJSON streaming.


Stream directory

Stream directory entries as NDJSON (newline-delimited JSON). Ideal for large directories - entries flow to the client as they're discovered, without accumulating in memory.

for await (const entry of rt.files.stream({
  dirPath: '/app',
  includeContent: true,
  codeFilesOnly: true,
})) {
  console.log(entry.name, entry.path);
}
GET https://workspace.oblien.com/files/stream?path=/app&include_content=true&code_files_only=true
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...

Response: Content-Type: application/x-ndjson

curl -N "https://workspace.oblien.com/files/stream?path=/app&include_content=true&code_files_only=true" \
  -H "Authorization: Bearer $GATEWAY_JWT"

Parameters

Same as List directory. The nested and flatten options are always enabled for streaming.

Response format

Each line is a JSON object. The stream starts with a start event and ends with a done event:

{"event":"start","path":"/app"}
{"name":"main.go","path":"/app/main.go","type":"file","size":1234}
{"name":"utils.go","path":"/app/utils.go","type":"file","size":567}
{"event":"done","count":2}

The stream endpoint uses batched directory reads for memory efficiency. Entries are not sorted - they arrive in filesystem order. Use the list endpoint if you need sorted output.


Read file

Read the content of a file. Supports line ranges for partial reads.

const file = await rt.files.read({
  filePath: '/app/src/main.go',
});

console.log(file.content);    // file content as string
console.log(file.lines);      // number of lines returned
console.log(file.size);       // file size in bytes

// Read specific line range
const partial = await rt.files.read({
  filePath: '/app/src/main.go',
  startLine: 10,
  endLine: 25,
  withLineNumbers: true,
});
GET https://workspace.oblien.com/files/read?path=/app/src/main.go&start_line=10&end_line=25&with_line_numbers=true
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
curl "https://workspace.oblien.com/files/read?path=/app/src/main.go&start_line=10&end_line=25&with_line_numbers=true" \
  -H "Authorization: Bearer $GATEWAY_JWT"

Parameters

ParameterTypeRequiredDescription
pathstringYesAbsolute path to the file
start_lineintegerNoFirst line to read (1-based)
end_lineintegerNoLast line to read (1-based, inclusive)
with_line_numbersbooleanNoPrefix each line with its line number

Response

{
  "success": true,
  "path": "/app/src/main.go",
  "content": "package main\n\nfunc main() {\n\tfmt.Println(\"hello\")\n}",
  "size": 1234,
  "lines": 5,
  "extension": ".go",
  "start_line": 10,
  "end_line": 25
}

start_line and end_line are only included when a line range was requested.


Write file

Create or overwrite a file. Uses atomic write (temp file + rename) by default. Accepts both POST and PUT.

const result = await rt.files.write({
  fullPath: '/app/src/hello.txt',
  content: 'Hello, world!',
  createDirs: true,
});

console.log(result.path);  // "/app/src/hello.txt"
console.log(result.size);  // 13

// Append to an existing file
await rt.files.write({
  fullPath: '/app/logs/output.log',
  content: 'New log entry\n',
  append: true,
  createDirs: true,
});
POST https://workspace.oblien.com/files/write
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
Content-Type: application/json

{
  "path": "/app/src/hello.txt",
  "content": "Hello, world!",
  "create_dirs": true
}
curl -X POST "https://workspace.oblien.com/files/write" \
  -H "Authorization: Bearer $GATEWAY_JWT" \
  -H "Content-Type: application/json" \
  -d '{"path":"/app/src/hello.txt","content":"Hello, world!","create_dirs":true}'

Parameters

ParameterTypeRequiredDescription
pathstringYesAbsolute path for the file
contentstringYesFile content
create_dirsbooleanNoCreate parent directories if they don't exist. Default false
appendbooleanNoAppend to existing file instead of overwriting. Default false
modestringNoFile permissions in octal (e.g. "0644"). Default "0644"

Response

{
  "success": true,
  "path": "/app/src/hello.txt",
  "size": 13
}

HTTP status: 201 Created


Create directory

Create a directory and any necessary parent directories.

await rt.files.mkdir({
  path: '/app/src/utils/helpers',
});
POST https://workspace.oblien.com/files/mkdir
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
Content-Type: application/json

{
  "path": "/app/src/utils/helpers"
}
curl -X POST "https://workspace.oblien.com/files/mkdir" \
  -H "Authorization: Bearer $GATEWAY_JWT" \
  -H "Content-Type: application/json" \
  -d '{"path":"/app/src/utils/helpers"}'

Parameters

ParameterTypeRequiredDescription
pathstringYesDirectory path to create
modestringNoDirectory permissions in octal (e.g. "0755"). Default "0755"

Response

{
  "success": true,
  "path": "/app/src/utils/helpers"
}

HTTP status: 201 Created


Stat

Get detailed information about a file or directory.

const info = await rt.files.stat({
  path: '/app/src/main.go',
});

console.log(info.type);        // "file"
console.log(info.size);        // 1234
console.log(info.permissions); // "0644"
console.log(info.is_code);     // true
GET https://workspace.oblien.com/files/stat?path=/app/src/main.go
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
curl "https://workspace.oblien.com/files/stat?path=/app/src/main.go" \
  -H "Authorization: Bearer $GATEWAY_JWT"

Parameters

ParameterTypeRequiredDescription
pathstringYesPath to the file or directory

Response

{
  "success": true,
  "path": "/app/src/main.go",
  "name": "main.go",
  "type": "file",
  "size": 1234,
  "modified": "2025-01-15T10:30:00Z",
  "permissions": "0644",
  "is_code": true,
  "extension": ".go"
}

For symlinks:

{
  "success": true,
  "path": "/app/link",
  "name": "link",
  "type": "symlink",
  "size": 0,
  "modified": "2025-01-15T10:30:00Z",
  "permissions": "0777",
  "is_code": false,
  "symlink_target": "/app/src/main.go"
}

Delete

Delete a file or directory. Directories are removed recursively.

await rt.files.delete({
  path: '/app/src/old-file.txt',
});
DELETE https://workspace.oblien.com/files/delete?path=/app/src/old-file.txt
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
curl -X DELETE "https://workspace.oblien.com/files/delete?path=/app/src/old-file.txt" \
  -H "Authorization: Bearer $GATEWAY_JWT"

Parameters

ParameterTypeRequiredDescription
pathstringYesPath to the file or directory to delete

The path can also be provided in the request body as {"path": "..."}.

Response

{
  "success": true,
  "path": "/app/src/old-file.txt"
}

System paths (/, /bin, /sbin, /usr, /lib, /lib64, /etc, /dev, /proc, /sys, /boot, /run) are protected and cannot be deleted.


Error responses

All file endpoints return errors in a consistent format:

{
  "error": "file not found: /app/missing.txt"
}
StatusMeaning
400Invalid parameters (missing path, path is a directory when file expected, etc.)
401Missing or invalid token
403Attempted to delete a protected system path
404File or directory not found
413File too large to read or content too large to write
500Internal server error

File Transfer

Stream files to and from the workspace using tar.gz archives. Supports multiple paths and exclude patterns — ideal for bulk download/upload.

Download

Download files and directories as a streaming tar.gz archive.

const rt = await client.workspaces.runtime('ws_a1b2c3d4');

const response = await rt.transfer.download({
  paths: ['/app/src', '/app/package.json'],
  excludePatterns: ['node_modules', '.git'],
});

// Save the tar.gz stream to a file (Node.js)
import { createWriteStream } from 'fs';
import { Readable } from 'stream';

const dest = createWriteStream('backup.tar.gz');
Readable.fromWeb(response.body).pipe(dest);
POST https://workspace.oblien.com/files/transfer/download
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
Content-Type: application/json

{
  "paths": ["/app/src", "/app/package.json"],
  "exclude_patterns": ["node_modules", ".git"]
}
curl -X POST "https://workspace.oblien.com/files/transfer/download" \
  -H "Authorization: Bearer $GATEWAY_JWT" \
  -H "Content-Type: application/json" \
  -d '{ "paths": ["/app/src"], "exclude_patterns": ["node_modules"] }' \
  -o backup.tar.gz

Parameters

ParameterTypeRequiredDescription
pathsstring[]YesAbsolute paths to include in the archive
exclude_patternsstring[]NoGlob patterns for filenames to exclude

Response

The response body is a streaming application/gzip tar archive (Content-Type: application/gzip).


Upload

Upload a tar.gz archive and extract it into the workspace.

import { readFileSync } from 'fs';

const rt = await client.workspaces.runtime('ws_a1b2c3d4');

const tarData = readFileSync('project.tar.gz');
const result = await rt.transfer.upload({
  body: tarData,
  dest: '/app',
});
console.log(result.files_extracted); // number of files extracted
POST https://workspace.oblien.com/files/transfer/upload?dest=/app
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
Content-Type: application/gzip

<raw tar.gz binary data>
curl -X POST "https://workspace.oblien.com/files/transfer/upload?dest=/app" \
  -H "Authorization: Bearer $GATEWAY_JWT" \
  -H "Content-Type: application/gzip" \
  --data-binary @project.tar.gz

Parameters

ParameterTypeRequiredDescription
deststring (query)NoDestination directory for extraction. Default: /

Response

{
  "success": true,
  "dest": "/app",
  "files_extracted": 42
}

CLI Push / Pull

The CLI provides push and pull shortcuts for file transfer:

# Push local files into the workspace
oblien files push ws_abc123 ./src ./package.json --dest /app

# Pull workspace files to local machine
oblien files pull ws_abc123 /app/src /app/config.json --dest ./backup

# Exclude patterns on pull
oblien files pull ws_abc123 /app --dest ./backup --exclude node_modules --exclude .git