proto: freeze the wire contract at 1.0.0

Schemas for the whole v1 surface: 38 methods, 9 events, 25 named types and the
JSON-RPC envelope, with x-privileged / x-transports / x-deadlineMs / x-errors
annotations that both generators emit as data rather than prose.

Four generators over one IR (contracts/codegen/schema_ir.py), so the C++ structs,
the TypeScript types and the OpenRPC document cannot disagree about what the
contract says:

  gen_cpp.py             -> core/generated/velox_proto.{hpp,cpp}
  gen_ts.py              -> extension/src/shared/protocol/
  gen_openrpc.py         -> contracts/openrpc.json
  gen_cpp_conformance.py -> tests/conformance/cpp/fixture_dispatcher.hpp

Inbound parsing never throws: parse<T>() returns std::expected<T, ParseError> and
nlohmann's throwing ADL from_json is deliberately not emitted. Schema constraints
(minimum, maxLength, pattern, ...) become real runtime checks in both languages —
the daemon does not trust the extension and the extension does not trust the
daemon.

59 golden fixtures: a success case per method, 12 error cases, 9 events. Replayed
by tests/conformance/ against both the generated C++ and a live server over both
transports. tools/mockd serves the same fixtures with unhappy-path flags so the
GUI and EXT lanes never wait for veloxd.

run.sh also proves capture.offer fails open: with a daemon answering slower than
750 ms the client gives up and lets Firefox take the download.

core/generated/ is libveloxproto, a separate target from libveloxcore, which
still never sees JSON — see docs/adr/0009.

Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_012fgjnqFCS5h5L7gZTZo3rV
This commit is contained in:
2026-09-09 19:55:54 +04:00
co-authored by Claude Opus 5
parent a40585f419
commit 53421d6cb8
171 changed files with 29275 additions and 51 deletions
+72
View File
@@ -0,0 +1,72 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/envelope.schema.json",
"title": "Envelope",
"description": "JSON-RPC 2.0 envelope as Velox uses it, plus the project's error codes. Every transport (UDS NDJSON, native-messaging length-prefix, loopback WebSocket) carries exactly these payloads; only the framing differs.",
"$defs": {
"Id": {
"title": "Id",
"description": "Request correlation id. Velox clients always send an integer; string ids are accepted for spec compliance. A notification has no id.",
"type": ["integer", "string"]
},
"Request": {
"title": "Request",
"type": "object",
"additionalProperties": false,
"required": ["jsonrpc", "method"],
"properties": {
"jsonrpc": { "const": "2.0" },
"id": { "$ref": "#/$defs/Id" },
"method": { "type": "string" },
"params": { "type": "object" }
}
},
"Response": {
"title": "Response",
"description": "Exactly one of result or error is present.",
"type": "object",
"additionalProperties": false,
"required": ["jsonrpc", "id"],
"properties": {
"jsonrpc": { "const": "2.0" },
"id": { "$ref": "#/$defs/Id" },
"result": {},
"error": { "$ref": "#/$defs/Error" }
}
},
"Notification": {
"title": "Notification",
"description": "Server to client. No id, never answered.",
"type": "object",
"additionalProperties": false,
"required": ["jsonrpc", "method", "params"],
"properties": {
"jsonrpc": { "const": "2.0" },
"method": { "type": "string" },
"params": { "type": "object" }
}
},
"Error": {
"title": "Error",
"type": "object",
"additionalProperties": false,
"required": ["code", "message"],
"properties": {
"code": { "$ref": "https://velox.dev/schema/types/ErrorCode.schema.json" },
"message": { "type": "string" },
"data": {
"type": ["object", "null"],
"description": "Code-specific detail. -32013 carries data.httpStatus; -32011 carries data.path; -32014 carries data.retryAfterSec.",
"properties": {
"httpStatus": { "type": ["integer", "null"] },
"path": { "type": ["string", "null"] },
"retryAfterSec": { "type": ["integer", "null"] },
"taskId": { "type": ["string", "null"] },
"expected": { "type": ["string", "null"] },
"actual": { "type": ["string", "null"] }
}
}
}
}
}
}
@@ -0,0 +1,44 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/events/event.auth.required.schema.json",
"title": "event.auth.required",
"description": "A server asked for credentials. The task sits in retry_wait until the client supplies them. Credentials travel to the Secret Service, never back through this event and never into a log.",
"x-direction": "server-to-client",
"type": "object",
"properties": {
"params": {
"type": "object",
"additionalProperties": false,
"required": [
"taskId",
"host",
"scheme"
],
"properties": {
"taskId": {
"type": "string",
"format": "uuid"
},
"host": {
"type": "string"
},
"realm": {
"type": [
"string",
"null"
]
},
"scheme": {
"type": "string",
"enum": [
"basic",
"digest",
"ntlm",
"negotiate",
"proxy"
]
}
}
}
}
}
@@ -0,0 +1,44 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/events/event.grabber.progress.schema.json",
"title": "event.grabber.progress",
"description": "Crawl progress for the Site Grabber wizard. done true means the file list in grabber.status is final.",
"x-direction": "server-to-client",
"x-maxRateHz": 4,
"type": "object",
"properties": {
"params": {
"type": "object",
"additionalProperties": false,
"required": [
"jobId",
"found",
"crawled",
"done"
],
"properties": {
"jobId": {
"type": "string"
},
"found": {
"type": "integer",
"minimum": 0
},
"crawled": {
"type": "integer",
"minimum": 0
},
"done": {
"type": "boolean"
},
"currentUrl": {
"type": [
"string",
"null"
],
"format": "uri"
}
}
}
}
}
@@ -0,0 +1,57 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/events/event.notify.schema.json",
"title": "event.notify",
"description": "Something the user should see: a completion, a failure, a queue finishing. The client decides between a toast, a tray balloon and a sound; the daemon does not assume a GUI is running.",
"x-direction": "server-to-client",
"type": "object",
"properties": {
"params": {
"type": "object",
"additionalProperties": false,
"required": [
"level",
"title",
"body"
],
"properties": {
"level": {
"type": "string",
"enum": [
"info",
"success",
"warning",
"error"
]
},
"title": {
"type": "string",
"maxLength": 128
},
"body": {
"type": "string",
"maxLength": 1024
},
"taskId": {
"type": [
"string",
"null"
],
"format": "uuid"
},
"sound": {
"type": [
"string",
"null"
],
"enum": [
"complete",
"queueComplete",
"error",
null
]
}
}
}
}
}
@@ -0,0 +1,25 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/events/event.settings.changed.schema.json",
"title": "event.settings.changed",
"description": "Settings were written by some client. Carries only the key names; a client re-reads what it cares about. The extension watches for capture.* here and re-fetches capture.getRules so its rules never lag the daemon's.",
"x-direction": "server-to-client",
"type": "object",
"properties": {
"params": {
"type": "object",
"additionalProperties": false,
"required": [
"keys"
],
"properties": {
"keys": {
"type": "array",
"items": {
"$ref": "https://velox.dev/schema/types/SettingKey.schema.json"
}
}
}
}
}
}
@@ -0,0 +1,41 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/events/event.speed.global.schema.json",
"title": "event.speed.global",
"description": "Aggregate throughput for the status bar, the tray tooltip and the extension popup. Emitted at 1 Hz even when nothing is active, so a client can tell 'idle' from 'disconnected'.",
"x-direction": "server-to-client",
"x-maxRateHz": 1,
"type": "object",
"properties": {
"params": {
"type": "object",
"additionalProperties": false,
"required": [
"downBps",
"activeCount"
],
"properties": {
"downBps": {
"type": "integer",
"minimum": 0
},
"activeCount": {
"type": "integer",
"minimum": 0
},
"queuedCount": {
"type": "integer",
"minimum": 0
},
"limitBps": {
"type": [
"integer",
"null"
],
"minimum": 0,
"description": "null when the limiter is off."
}
}
}
}
}
@@ -0,0 +1,27 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/events/event.task.added.schema.json",
"title": "event.task.added",
"description": "A task entered the list. summary is always present so a client can insert the row without a follow-up download.get.",
"x-direction": "server-to-client",
"type": "object",
"properties": {
"params": {
"type": "object",
"additionalProperties": false,
"required": [
"taskId",
"summary"
],
"properties": {
"taskId": {
"type": "string",
"format": "uuid"
},
"summary": {
"$ref": "https://velox.dev/schema/types/TaskSummary.schema.json"
}
}
}
}
}
@@ -0,0 +1,87 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/events/event.task.progress.schema.json",
"title": "event.task.progress",
"description": "Batched byte counters for every active task. Emitted at no more than 4 Hz as one array, never one notification per task: at twenty active downloads that is four messages a second instead of eighty. Clients apply a row patch and repaint the touched columns; rebuilding a model on this event is a bug.",
"x-direction": "server-to-client",
"x-maxRateHz": 4,
"type": "object",
"properties": {
"params": {
"type": "object",
"additionalProperties": false,
"required": [
"tasks",
"at"
],
"properties": {
"tasks": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": false,
"required": [
"taskId",
"downloadedBytes",
"speedBps"
],
"properties": {
"taskId": {
"type": "string",
"format": "uuid"
},
"downloadedBytes": {
"type": "integer",
"minimum": 0
},
"speedBps": {
"type": "integer",
"minimum": 0
},
"etaSeconds": {
"type": [
"integer",
"null"
],
"minimum": 0
},
"segments": {
"type": "array",
"maxItems": 32,
"items": {
"type": "object",
"additionalProperties": false,
"description": "Only what a segment bar needs. Full segment state comes from download.get.",
"required": [
"index",
"downloadedBytes",
"speedBps"
],
"properties": {
"index": {
"type": "integer",
"minimum": 0,
"maximum": 31
},
"downloadedBytes": {
"type": "integer",
"minimum": 0
},
"speedBps": {
"type": "integer",
"minimum": 0
}
}
}
}
}
}
},
"at": {
"type": "string",
"format": "date-time"
}
}
}
}
}
@@ -0,0 +1,27 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/events/event.task.removed.schema.json",
"title": "event.task.removed",
"description": "A task left the list. The client deletes the row; there is nothing further to fetch.",
"x-direction": "server-to-client",
"type": "object",
"properties": {
"params": {
"type": "object",
"additionalProperties": false,
"required": [
"taskId",
"deletedFile"
],
"properties": {
"taskId": {
"type": "string",
"format": "uuid"
},
"deletedFile": {
"type": "boolean"
}
}
}
}
}
@@ -0,0 +1,57 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/events/event.task.state.schema.json",
"title": "event.task.state",
"description": "A task changed lifecycle state. Carries the summary so the row can be repainted in full without a round trip, and error whenever the new state is failed or retry_wait.",
"x-direction": "server-to-client",
"type": "object",
"properties": {
"params": {
"type": "object",
"additionalProperties": false,
"required": [
"taskId",
"state"
],
"properties": {
"taskId": {
"type": "string",
"format": "uuid"
},
"state": {
"$ref": "https://velox.dev/schema/types/TaskState.schema.json"
},
"previousState": {
"oneOf": [
{
"$ref": "https://velox.dev/schema/types/TaskState.schema.json"
},
{
"type": "null"
}
]
},
"summary": {
"oneOf": [
{
"$ref": "https://velox.dev/schema/types/TaskSummary.schema.json"
},
{
"type": "null"
}
]
},
"error": {
"oneOf": [
{
"$ref": "https://velox.dev/schema/types/TaskError.schema.json"
},
{
"type": "null"
}
]
}
}
}
}
}
@@ -0,0 +1,23 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/methods/capture.getRules.schema.json",
"title": "capture.getRules",
"description": "The daemon's capture policy, so the extension's shouldCapture decision cannot drift from the daemon's. Fetched on connect and whenever event.settings.changed names a capture.* key. If this call fails the extension keeps its last known rules and stays fail-open.",
"x-privileged": false,
"x-transports": [
"uds",
"ws"
],
"x-deadlineMs": 2000,
"type": "object",
"properties": {
"params": {
"type": "object",
"additionalProperties": false,
"properties": {}
},
"result": {
"$ref": "https://velox.dev/schema/types/CaptureRules.schema.json"
}
}
}
@@ -2,10 +2,11 @@
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/methods/capture.offer.schema.json",
"title": "capture.offer",
"description": "Firefox offers an intercepted response to the daemon. The daemon MUST reply within 750 ms; the extension abandons the offer and lets Firefox download normally on timeout. TEMPLATE — lane PROTO owns the final shape.",
"description": "Firefox offers an intercepted response to the daemon. The daemon MUST reply within 750 ms; the extension abandons the offer and lets Firefox download normally on timeout. This deadline is the whole reason capture fails open, and it is conformance-tested: a daemon that is slow, down, or erroring must never cost the user a download.",
"x-privileged": false,
"x-transports": ["uds", "ws"],
"x-deadlineMs": 750,
"x-errors": [-32011],
"type": "object",
"properties": {
"params": {
@@ -16,32 +17,20 @@
"url": { "type": "string", "format": "uri" },
"method": { "type": "string", "enum": ["GET", "POST"] },
"tabUrl": { "type": "string", "format": "uri" },
"headers": {
"type": "object",
"description": "Request headers Firefox was about to send, verbatim. Needed for signed-URL and referrer-gated CDNs.",
"additionalProperties": { "type": "string" }
},
"headers": { "oneOf": [{ "$ref": "https://velox.dev/schema/types/Headers.schema.json" }, { "type": "null" }] },
"cookies": {
"type": "array",
"type": ["array", "null"],
"description": "Cookies for the URL, so authenticated downloads work outside the browser.",
"items": {
"type": "object",
"required": ["name", "value"],
"properties": {
"name": { "type": "string" },
"value": { "type": "string" },
"domain": { "type": "string" },
"path": { "type": "string" }
}
}
"items": { "$ref": "https://velox.dev/schema/types/Cookie.schema.json" }
},
"contentType": { "type": ["string", "null"] },
"contentLength": { "type": ["integer", "null"], "minimum": 0 },
"contentDisposition": { "type": ["string", "null"] },
"filename": { "type": ["string", "null"], "description": "Extension's best guess; the daemon may override" },
"filename": { "type": ["string", "null"], "description": "The extension's best guess; the daemon may override it." },
"userAgent": { "type": ["string", "null"] },
"referrer": { "type": ["string", "null"] },
"origin": { "type": "string", "description": "moz-extension://… — the daemon verifies this on the WS transport" }
"origin": { "type": ["string", "null"], "description": "moz-extension://... The daemon verifies this on the WS transport and refuses anything else." },
"requestId": { "type": ["string", "null"], "description": "The extension's webRequest id, echoed in logs so a capture decision can be traced back to one browser request." }
}
},
"result": {
@@ -50,11 +39,12 @@
"required": ["action"],
"properties": {
"action": { "type": "string", "enum": ["take", "ignore"] },
"taskId": { "type": ["string", "null"], "format": "uuid" },
"taskId": { "type": ["string", "null"], "format": "uuid", "description": "Set when action is 'take'." },
"reason": {
"type": ["string", "null"],
"description": "Why the offer was declined. Set when action is 'ignore'; the extension logs it in the popup's diagnostics.",
"enum": ["excluded_host", "type_not_monitored", "below_min_size", "duplicate",
"capture_disabled", "user_declined", null]
"capture_disabled", "user_declined", "rule_ignore", null]
}
}
}
@@ -0,0 +1,35 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/methods/category.list.schema.json",
"title": "category.list",
"description": "Every category with its folder and extension list. The extension calls this to populate its default-category picker, which is why it is not privileged; it is read-only and exposes only paths the user already configured.",
"x-privileged": false,
"x-transports": [
"uds",
"ws"
],
"x-deadlineMs": 2000,
"type": "object",
"properties": {
"params": {
"type": "object",
"additionalProperties": false,
"properties": {}
},
"result": {
"type": "object",
"additionalProperties": false,
"required": [
"items"
],
"properties": {
"items": {
"type": "array",
"items": {
"$ref": "https://velox.dev/schema/types/Category.schema.json"
}
}
}
}
}
}
@@ -0,0 +1,56 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/methods/category.remove.schema.json",
"title": "category.remove",
"description": "Delete a user-created category. Built-in categories are refused with -32602. Tasks filed under it are reassigned to reassignTo, or to the default category when that is null; no task is ever orphaned.",
"x-privileged": true,
"x-transports": [
"uds"
],
"x-deadlineMs": 5000,
"x-errors": [
-32003,
-32602
],
"type": "object",
"properties": {
"params": {
"type": "object",
"additionalProperties": false,
"required": [
"categoryId"
],
"properties": {
"categoryId": {
"type": "string"
},
"reassignTo": {
"type": [
"string",
"null"
]
}
}
},
"result": {
"type": "object",
"additionalProperties": false,
"required": [
"removed",
"reassignedTaskIds"
],
"properties": {
"removed": {
"type": "boolean"
},
"reassignedTaskIds": {
"type": "array",
"items": {
"type": "string",
"format": "uuid"
}
}
}
}
}
}
@@ -0,0 +1,43 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/methods/category.upsert.schema.json",
"title": "category.upsert",
"description": "Create or replace a category. Omit categoryId to create; supply it to replace. Changing saveDir does not move existing files \u2014 the GUI asks separately and issues download.update per task, so a re-point is never a surprise mass file move.",
"x-privileged": true,
"x-transports": [
"uds"
],
"x-deadlineMs": 5000,
"x-errors": [
-32003,
-32011
],
"type": "object",
"properties": {
"params": {
"type": "object",
"additionalProperties": false,
"required": [
"category"
],
"properties": {
"category": {
"$ref": "https://velox.dev/schema/types/Category.schema.json"
}
}
},
"result": {
"type": "object",
"additionalProperties": false,
"description": "The stored category, with categoryId filled in on create.",
"required": [
"category"
],
"properties": {
"category": {
"$ref": "https://velox.dev/schema/types/Category.schema.json"
}
}
}
}
}
@@ -0,0 +1,25 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/methods/download.add.schema.json",
"title": "download.add",
"description": "Create one task. saveDir is canonicalized and checked against saveTo.allowedRoots before anything is written; a path that escapes them is refused with -32011 and no file is created.",
"x-privileged": false,
"x-transports": ["uds", "ws"],
"x-deadlineMs": 5000,
"x-errors": [-32011, -32012, -32013],
"x-wsRestrictions": ["saveDir must be absent or resolve inside an existing category folder; anything else is -32011. The extension may request a download, it may not choose an arbitrary destination."],
"type": "object",
"properties": {
"params": { "$ref": "https://velox.dev/schema/types/DownloadSpec.schema.json" },
"result": {
"type": "object",
"additionalProperties": false,
"required": ["taskId", "state"],
"properties": {
"taskId": { "type": "string", "format": "uuid" },
"state": { "$ref": "https://velox.dev/schema/types/TaskState.schema.json" },
"duplicate": { "type": ["string", "null"], "format": "uuid", "description": "The existing task this URL matched, when downloads.duplicatePolicy resolved to 'skip'. taskId then names that existing task." }
}
}
}
}
@@ -0,0 +1,45 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/methods/download.addBatch.schema.json",
"title": "download.addBatch",
"description": "Create many tasks in one call: the clipboard blob, the wildcard expander, and the extension's 'Download all links'. Partial success is normal and is reported per item rather than failing the whole batch.",
"x-privileged": false,
"x-transports": ["uds", "ws"],
"x-deadlineMs": 30000,
"x-errors": [-32011, -32012],
"x-wsRestrictions": ["Same saveDir restriction as download.add, applied to defaults and to every item."],
"type": "object",
"properties": {
"params": {
"type": "object",
"additionalProperties": false,
"required": ["items"],
"properties": {
"items": { "type": "array", "minItems": 1, "maxItems": 5000, "items": { "$ref": "https://velox.dev/schema/types/DownloadSpec.schema.json" } },
"defaults": { "oneOf": [{ "$ref": "https://velox.dev/schema/types/DownloadSpec.schema.json" }, { "type": "null" }], "description": "Applied to any field an item left unset. Its url is ignored." }
}
},
"result": {
"type": "object",
"additionalProperties": false,
"required": ["taskIds", "failed"],
"properties": {
"taskIds": { "type": "array", "items": { "type": "string", "format": "uuid" }, "description": "In the same order as the accepted items." },
"failed": {
"type": "array",
"description": "One entry per item that could not be added. index refers to params.items.",
"items": {
"type": "object",
"additionalProperties": false,
"required": ["index", "code", "message"],
"properties": {
"index": { "type": "integer", "minimum": 0 },
"code": { "$ref": "https://velox.dev/schema/types/ErrorCode.schema.json" },
"message": { "type": "string" }
}
}
}
}
}
}
}
@@ -0,0 +1,39 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/methods/download.cancel.schema.json",
"title": "download.cancel",
"description": "Stop the given tasks and mark them cancelled. The .veloxpart file is kept so the user can still resume from the list; download.remove is what deletes bytes.",
"x-privileged": false,
"x-transports": [
"uds",
"ws"
],
"x-deadlineMs": 5000,
"x-errors": [
-32010
],
"type": "object",
"properties": {
"params": {
"type": "object",
"additionalProperties": false,
"required": [
"taskIds"
],
"properties": {
"taskIds": {
"type": "array",
"minItems": 1,
"maxItems": 5000,
"items": {
"type": "string",
"format": "uuid"
}
}
}
},
"result": {
"$ref": "https://velox.dev/schema/types/BulkTaskResult.schema.json"
}
}
}
@@ -0,0 +1,20 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/methods/download.get.schema.json",
"title": "download.get",
"description": "Full detail for one task, including per-segment state. Backs the progress dialog. Poll it no faster than the progress dialog repaints; the table must use events instead.",
"x-privileged": false,
"x-transports": ["uds", "ws"],
"x-deadlineMs": 5000,
"x-errors": [-32010],
"type": "object",
"properties": {
"params": {
"type": "object",
"additionalProperties": false,
"required": ["taskId"],
"properties": { "taskId": { "type": "string", "format": "uuid" } }
},
"result": { "$ref": "https://velox.dev/schema/types/TaskDetail.schema.json" }
}
}
@@ -0,0 +1,31 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/methods/download.list.schema.json",
"title": "download.list",
"description": "The main table. Filtering, sorting and paging all happen in the daemon so the GUI never materializes 100k rows to show 40. Called once on connect; after that the table is maintained from events, never re-fetched on a progress tick.",
"x-privileged": false,
"x-transports": ["uds", "ws"],
"x-deadlineMs": 5000,
"type": "object",
"properties": {
"params": {
"type": "object",
"additionalProperties": false,
"properties": {
"filter": { "oneOf": [{ "$ref": "https://velox.dev/schema/types/TaskFilter.schema.json" }, { "type": "null" }] },
"sort": { "oneOf": [{ "$ref": "https://velox.dev/schema/types/TaskSort.schema.json" }, { "type": "null" }] },
"offset": { "type": ["integer", "null"], "minimum": 0 },
"limit": { "type": ["integer", "null"], "minimum": 1, "maximum": 5000, "description": "Defaults to 500. The GUI pages; the extension popup asks for far fewer." }
}
},
"result": {
"type": "object",
"additionalProperties": false,
"required": ["total", "items"],
"properties": {
"total": { "type": "integer", "minimum": 0, "description": "Rows matching the filter, ignoring offset and limit." },
"items": { "type": "array", "items": { "$ref": "https://velox.dev/schema/types/TaskSummary.schema.json" } }
}
}
}
}
@@ -0,0 +1,39 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/methods/download.pause.schema.json",
"title": "download.pause",
"description": "Suspend transfers and flush every segment's progress to the .veloxpart.meta file, so a pause is indistinguishable from a crash as far as resume is concerned. Never loses bytes already written.",
"x-privileged": false,
"x-transports": [
"uds",
"ws"
],
"x-deadlineMs": 5000,
"x-errors": [
-32010
],
"type": "object",
"properties": {
"params": {
"type": "object",
"additionalProperties": false,
"required": [
"taskIds"
],
"properties": {
"taskIds": {
"type": "array",
"minItems": 1,
"maxItems": 5000,
"items": {
"type": "string",
"format": "uuid"
}
}
}
},
"result": {
"$ref": "https://velox.dev/schema/types/BulkTaskResult.schema.json"
}
}
}
@@ -0,0 +1,44 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/methods/download.probe.schema.json",
"title": "download.probe",
"description": "Ask what is at a URL without creating a task. Populates the File Info dialog. Runs a HEAD, falling back to a ranged GET when HEAD is refused, which is also how resumability is established. Never blocks the RPC loop; the dialog opens immediately and fills in when this lands.",
"x-privileged": false,
"x-transports": ["uds", "ws"],
"x-deadlineMs": 30000,
"x-errors": [-32013],
"type": "object",
"properties": {
"params": {
"type": "object",
"additionalProperties": false,
"required": ["url"],
"properties": {
"url": { "type": "string", "format": "uri" },
"headers": { "oneOf": [{ "$ref": "https://velox.dev/schema/types/Headers.schema.json" }, { "type": "null" }] },
"cookies": { "type": ["array", "null"], "items": { "$ref": "https://velox.dev/schema/types/Cookie.schema.json" } },
"referrer": { "type": ["string", "null"] },
"userAgent": { "type": ["string", "null"] }
}
},
"result": {
"type": "object",
"additionalProperties": false,
"required": ["filename", "mime", "resumable", "effectiveUrl", "suggestedCategoryId"],
"properties": {
"filename": { "type": "string", "description": "From Content-Disposition when present, else the URL path, sanitized." },
"sizeBytes": { "type": ["integer", "null"], "minimum": 0 },
"mime": { "type": "string" },
"resumable": { "type": "boolean", "description": "Accept-Ranges: bytes and a validator (ETag or Last-Modified) are both present." },
"effectiveUrl": { "type": "string", "format": "uri" },
"suggestedCategoryId": { "type": "string", "description": "What the rules engine would pick. The dialog preselects it; the user may override." },
"suggestedSaveDir": { "type": ["string", "null"] },
"etag": { "type": ["string", "null"] },
"lastModified": { "type": ["string", "null"] },
"acceptRanges": { "type": "boolean" },
"redirectChain": { "type": "array", "items": { "type": "string", "format": "uri" }, "description": "Every hop, so the user can see where a shortener actually led." },
"requiresAuth": { "type": "boolean", "description": "The probe got a 401/407. The GUI should collect credentials before adding." }
}
}
}
}
@@ -0,0 +1,36 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/methods/download.refreshUrl.schema.json",
"title": "download.refreshUrl",
"description": "IDM's 'Refresh Download Address'. Point an existing task at a freshly-issued URL when a signed link has expired, keeping every byte already on disk. The daemon re-probes and compares size and validator: if they still match, the transfer resumes from where it stopped; if they do not, it says so rather than silently restarting.",
"x-privileged": false,
"x-transports": ["uds", "ws"],
"x-deadlineMs": 30000,
"x-errors": [-32010, -32013],
"type": "object",
"properties": {
"params": {
"type": "object",
"additionalProperties": false,
"required": ["taskId", "url"],
"properties": {
"taskId": { "type": "string", "format": "uuid" },
"url": { "type": "string", "format": "uri" },
"headers": { "oneOf": [{ "$ref": "https://velox.dev/schema/types/Headers.schema.json" }, { "type": "null" }] },
"cookies": { "type": ["array", "null"], "items": { "$ref": "https://velox.dev/schema/types/Cookie.schema.json" } }
}
},
"result": {
"type": "object",
"additionalProperties": false,
"required": ["ok", "resumable", "contentChanged"],
"properties": {
"ok": { "type": "boolean" },
"resumable": { "type": "boolean" },
"contentChanged": { "type": "boolean", "description": "true when size or validator differ from what was recorded. The GUI must ask before restarting from zero — never discard bytes without consent." },
"sizeBytes": { "type": ["integer", "null"], "minimum": 0 },
"effectiveUrl": { "type": ["string", "null"], "format": "uri" }
}
}
}
}
@@ -0,0 +1,43 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/methods/download.remove.schema.json",
"title": "download.remove",
"description": "Drop tasks from the list, optionally deleting the bytes on disk. Privileged: this is the only method that destroys user data, and the extension is never allowed to reach it. The daemon deletes the .veloxpart and .veloxpart.meta pair, and the finished file only when deleteFile is true.",
"x-privileged": true,
"x-transports": ["uds"],
"x-deadlineMs": 10000,
"x-errors": [-32003, -32010],
"type": "object",
"properties": {
"params": {
"type": "object",
"additionalProperties": false,
"required": ["taskIds", "deleteFile"],
"properties": {
"taskIds": { "type": "array", "minItems": 1, "maxItems": 5000, "items": { "type": "string", "format": "uuid" } },
"deleteFile": { "type": "boolean", "description": "Explicit and required — there is no default for deleting a user's file." }
}
},
"result": {
"type": "object",
"additionalProperties": false,
"required": ["removed", "failed"],
"properties": {
"removed": { "type": "array", "items": { "type": "string", "format": "uuid" } },
"failed": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": false,
"required": ["taskId", "code", "message"],
"properties": {
"taskId": { "type": "string", "format": "uuid" },
"code": { "$ref": "https://velox.dev/schema/types/ErrorCode.schema.json" },
"message": { "type": "string" }
}
}
}
}
}
}
}
@@ -0,0 +1,39 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/methods/download.resume.schema.json",
"title": "download.resume",
"description": "Continue paused tasks. Resumption is revalidated with If-Range against the stored ETag or Last-Modified; a 200 where 206 was expected means the file changed on the server, and the task moves to failed with a clear error rather than corrupting the part file.",
"x-privileged": false,
"x-transports": [
"uds",
"ws"
],
"x-deadlineMs": 5000,
"x-errors": [
-32010
],
"type": "object",
"properties": {
"params": {
"type": "object",
"additionalProperties": false,
"required": [
"taskIds"
],
"properties": {
"taskIds": {
"type": "array",
"minItems": 1,
"maxItems": 5000,
"items": {
"type": "string",
"format": "uuid"
}
}
}
},
"result": {
"$ref": "https://velox.dev/schema/types/BulkTaskResult.schema.json"
}
}
}
@@ -0,0 +1,39 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/methods/download.start.schema.json",
"title": "download.start",
"description": "Begin or restart the given tasks. A task in 'queued' jumps its queue; a task already downloading is a no-op reported as changed false.",
"x-privileged": false,
"x-transports": [
"uds",
"ws"
],
"x-deadlineMs": 5000,
"x-errors": [
-32010
],
"type": "object",
"properties": {
"params": {
"type": "object",
"additionalProperties": false,
"required": [
"taskIds"
],
"properties": {
"taskIds": {
"type": "array",
"minItems": 1,
"maxItems": 5000,
"items": {
"type": "string",
"format": "uuid"
}
}
}
},
"result": {
"$ref": "https://velox.dev/schema/types/BulkTaskResult.schema.json"
}
}
}
@@ -0,0 +1,37 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/methods/download.update.schema.json",
"title": "download.update",
"description": "Change a task's mutable fields. Moving saveDir or filename moves the file on disk in the same operation, which is what makes dragging a row onto a category work as one RPC. Privileged: it can name a destination path.",
"x-privileged": true,
"x-transports": ["uds"],
"x-deadlineMs": 30000,
"x-errors": [-32003, -32010, -32011],
"type": "object",
"properties": {
"params": {
"type": "object",
"additionalProperties": false,
"required": ["taskId", "patch"],
"properties": {
"taskId": { "type": "string", "format": "uuid" },
"patch": {
"type": "object",
"additionalProperties": false,
"description": "Only the present fields change. An explicit null clears a nullable field.",
"properties": {
"filename": { "type": ["string", "null"], "maxLength": 255 },
"saveDir": { "type": ["string", "null"] },
"categoryId": { "type": ["string", "null"] },
"queueId": { "type": ["string", "null"] },
"description": { "type": ["string", "null"], "maxLength": 1024 },
"segments": { "type": ["integer", "null"], "minimum": 1, "maximum": 32, "description": "Takes effect on the next start; a running task is not re-segmented underneath the user." },
"bufferBytes": { "type": ["integer", "null"], "minimum": 4096, "maximum": 8388608 },
"checksum": { "oneOf": [{ "$ref": "https://velox.dev/schema/types/Checksum.schema.json" }, { "type": "null" }] }
}
}
}
},
"result": { "$ref": "https://velox.dev/schema/types/TaskSummary.schema.json" }
}
}
@@ -0,0 +1,89 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/methods/grabber.harvest.schema.json",
"title": "grabber.harvest",
"description": "Turn selected crawl results into tasks. This is the only grabber call that creates downloads, and it names exactly the files the user ticked \u2014 a crawl never starts a download on its own.",
"x-privileged": true,
"x-transports": [
"uds"
],
"x-deadlineMs": 30000,
"x-errors": [
-32003,
-32011
],
"type": "object",
"properties": {
"params": {
"type": "object",
"additionalProperties": false,
"required": [
"jobId",
"select"
],
"properties": {
"jobId": {
"type": "string"
},
"select": {
"type": "array",
"minItems": 1,
"items": {
"type": "string"
},
"description": "fileIds from grabber.status."
},
"defaults": {
"oneOf": [
{
"$ref": "https://velox.dev/schema/types/DownloadSpec.schema.json"
},
{
"type": "null"
}
]
}
}
},
"result": {
"type": "object",
"additionalProperties": false,
"required": [
"taskIds",
"failed"
],
"properties": {
"taskIds": {
"type": "array",
"items": {
"type": "string",
"format": "uuid"
}
},
"failed": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": false,
"required": [
"fileId",
"code",
"message"
],
"properties": {
"fileId": {
"type": "string"
},
"code": {
"$ref": "https://velox.dev/schema/types/ErrorCode.schema.json"
},
"message": {
"type": "string"
}
}
}
}
}
}
}
}
@@ -0,0 +1,106 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/methods/grabber.start.schema.json",
"title": "grabber.start",
"description": "Start a depth-limited crawl. Nothing is downloaded by this call: it only walks pages and collects candidate links, which the wizard then shows for selection. Privileged because an unbounded crawl is a resource commitment the browser must not be able to make on the user's behalf.",
"x-privileged": true,
"x-transports": [
"uds"
],
"x-deadlineMs": 5000,
"x-errors": [
-32003
],
"type": "object",
"properties": {
"params": {
"type": "object",
"additionalProperties": false,
"required": [
"startUrl",
"depth"
],
"properties": {
"startUrl": {
"type": "string",
"format": "uri"
},
"depth": {
"type": "integer",
"minimum": 0,
"maximum": 10
},
"includePatterns": {
"type": [
"array",
"null"
],
"items": {
"type": "string"
}
},
"excludePatterns": {
"type": [
"array",
"null"
],
"items": {
"type": "string"
}
},
"fileTypes": {
"type": [
"array",
"null"
],
"items": {
"type": "string"
},
"description": "Extensions, without the dot. null means every type."
},
"sameHostOnly": {
"type": "boolean"
},
"maxFiles": {
"type": [
"integer",
"null"
],
"minimum": 1,
"maximum": 10000
},
"headers": {
"oneOf": [
{
"$ref": "https://velox.dev/schema/types/Headers.schema.json"
},
{
"type": "null"
}
]
},
"cookies": {
"type": [
"array",
"null"
],
"items": {
"$ref": "https://velox.dev/schema/types/Cookie.schema.json"
}
}
}
},
"result": {
"type": "object",
"additionalProperties": false,
"required": [
"jobId"
],
"properties": {
"jobId": {
"type": "string"
}
}
}
}
}
@@ -0,0 +1,75 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/methods/grabber.status.schema.json",
"title": "grabber.status",
"description": "Poll one crawl. Also delivered as event.grabber.progress; the poll exists so the wizard can be reopened on a job it did not start and still catch up.",
"x-privileged": true,
"x-transports": [
"uds"
],
"x-deadlineMs": 5000,
"x-errors": [
-32003,
-32602
],
"type": "object",
"properties": {
"params": {
"type": "object",
"additionalProperties": false,
"required": [
"jobId"
],
"properties": {
"jobId": {
"type": "string"
}
}
},
"result": {
"type": "object",
"additionalProperties": false,
"required": [
"jobId",
"state",
"crawled",
"found",
"files"
],
"properties": {
"jobId": {
"type": "string"
},
"state": {
"type": "string",
"enum": [
"crawling",
"done",
"failed",
"cancelled"
]
},
"crawled": {
"type": "integer",
"minimum": 0
},
"found": {
"type": "integer",
"minimum": 0
},
"files": {
"type": "array",
"items": {
"$ref": "https://velox.dev/schema/types/GrabberFile.schema.json"
}
},
"error": {
"type": [
"string",
"null"
]
}
}
}
}
}
@@ -0,0 +1,25 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/methods/limiter.get.schema.json",
"title": "limiter.get",
"description": "Current global speed limit. Privileged: changing or reading the limiter belongs to the GUI and CLI; the extension shows throughput from event.speed.global instead.",
"x-privileged": true,
"x-transports": [
"uds"
],
"x-deadlineMs": 2000,
"x-errors": [
-32003
],
"type": "object",
"properties": {
"params": {
"type": "object",
"additionalProperties": false,
"properties": {}
},
"result": {
"$ref": "https://velox.dev/schema/types/Limiter.schema.json"
}
}
}
@@ -0,0 +1,24 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/methods/limiter.set.schema.json",
"title": "limiter.set",
"description": "Set the global token-bucket limit. With applyToRunning true the change re-tunes transfers already in flight instead of taking effect only on the next task \u2014 the Speed Limiter window's 'apply now' button.",
"x-privileged": true,
"x-transports": [
"uds"
],
"x-deadlineMs": 5000,
"x-errors": [
-32003,
-32602
],
"type": "object",
"properties": {
"params": {
"$ref": "https://velox.dev/schema/types/Limiter.schema.json"
},
"result": {
"$ref": "https://velox.dev/schema/types/Limiter.schema.json"
}
}
}
@@ -0,0 +1,82 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/methods/media.addVariant.schema.json",
"title": "media.addVariant",
"description": "Turn one enumerated variant into a task. The daemon fetches the segments in parallel and muxes them with ffmpeg; the result is an ordinary task that appears in the list like any other download. Refused with -32602 when the variant is DRM-protected.",
"x-privileged": false,
"x-transports": [
"uds",
"ws"
],
"x-deadlineMs": 30000,
"x-errors": [
-32011,
-32602,
-32013
],
"x-wsRestrictions": [
"Same saveDir restriction as download.add."
],
"type": "object",
"properties": {
"params": {
"type": "object",
"additionalProperties": false,
"description": "spec carries the same destination and queueing fields as download.add; its url is ignored because the manifest and variant determine the source.",
"required": [
"manifestUrl",
"variantId"
],
"properties": {
"manifestUrl": {
"type": "string",
"format": "uri"
},
"variantId": {
"type": "string"
},
"audioVariantId": {
"type": [
"string",
"null"
],
"description": "For DASH and HLS renditions where audio is a separate track to be muxed in."
},
"spec": {
"oneOf": [
{
"$ref": "https://velox.dev/schema/types/DownloadSpec.schema.json"
},
{
"type": "null"
}
]
}
}
},
"result": {
"type": "object",
"additionalProperties": false,
"required": [
"taskId",
"state"
],
"properties": {
"taskId": {
"type": "string",
"format": "uuid"
},
"state": {
"$ref": "https://velox.dev/schema/types/TaskState.schema.json"
},
"estimatedBytes": {
"type": [
"integer",
"null"
],
"minimum": 0
}
}
}
}
}
@@ -0,0 +1,97 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/methods/media.listVariants.schema.json",
"title": "media.listVariants",
"description": "Parse an HLS or DASH manifest in the daemon and enumerate its renditions. The extension never parses a manifest \u2014 that logic lives in one language, in one place. Variants with drm true are reported so the UI can grey them out; DRM-protected streams are refused, not attempted.",
"x-privileged": false,
"x-transports": [
"uds",
"ws"
],
"x-deadlineMs": 30000,
"x-errors": [
-32013
],
"type": "object",
"properties": {
"params": {
"type": "object",
"additionalProperties": false,
"required": [
"manifestUrl"
],
"properties": {
"manifestUrl": {
"type": "string",
"format": "uri"
},
"headers": {
"oneOf": [
{
"$ref": "https://velox.dev/schema/types/Headers.schema.json"
},
{
"type": "null"
}
]
},
"cookies": {
"type": [
"array",
"null"
],
"items": {
"$ref": "https://velox.dev/schema/types/Cookie.schema.json"
}
},
"referrer": {
"type": [
"string",
"null"
]
}
}
},
"result": {
"type": "object",
"additionalProperties": false,
"required": [
"variants",
"manifestType",
"drmProtected"
],
"properties": {
"variants": {
"type": "array",
"items": {
"$ref": "https://velox.dev/schema/types/MediaVariant.schema.json"
}
},
"manifestType": {
"type": "string",
"enum": [
"hls",
"dash"
]
},
"durationSec": {
"type": [
"number",
"null"
],
"minimum": 0
},
"title": {
"type": [
"string",
"null"
]
},
"drmProtected": {
"type": "boolean",
"description": "The manifest as a whole is DRM-protected. Refuse with a clear message rather than downloading undecryptable segments."
}
}
}
}
}
@@ -0,0 +1,35 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/methods/queue.list.schema.json",
"title": "queue.list",
"description": "Every queue with its run state and ordering. Not privileged: the extension's 'Add to Queue' picker needs it.",
"x-privileged": false,
"x-transports": [
"uds",
"ws"
],
"x-deadlineMs": 2000,
"type": "object",
"properties": {
"params": {
"type": "object",
"additionalProperties": false,
"properties": {}
},
"result": {
"type": "object",
"additionalProperties": false,
"required": [
"items"
],
"properties": {
"items": {
"type": "array",
"items": {
"$ref": "https://velox.dev/schema/types/Queue.schema.json"
}
}
}
}
}
}
@@ -0,0 +1,50 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/methods/queue.reorder.schema.json",
"title": "queue.reorder",
"description": "Rewrite a queue's run order. taskIds must be a permutation of the queue's current membership; anything else is -32602 rather than a partial reorder, so a stale drag from an out-of-date view cannot quietly reshuffle the queue.",
"x-privileged": true,
"x-transports": [
"uds"
],
"x-deadlineMs": 5000,
"x-errors": [
-32003,
-32602
],
"type": "object",
"properties": {
"params": {
"type": "object",
"additionalProperties": false,
"required": [
"queueId",
"taskIds"
],
"properties": {
"queueId": {
"type": "string"
},
"taskIds": {
"type": "array",
"items": {
"type": "string",
"format": "uuid"
}
}
}
},
"result": {
"type": "object",
"additionalProperties": false,
"required": [
"queue"
],
"properties": {
"queue": {
"$ref": "https://velox.dev/schema/types/Queue.schema.json"
}
}
}
}
}
@@ -0,0 +1,49 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/methods/queue.start.schema.json",
"title": "queue.start",
"description": "Start a queue running. The scheduler then admits up to maxConcurrent tasks from it, in order, and keeps that many running until the queue drains or is stopped.",
"x-privileged": true,
"x-transports": [
"uds"
],
"x-deadlineMs": 5000,
"x-errors": [
-32003
],
"type": "object",
"properties": {
"params": {
"type": "object",
"additionalProperties": false,
"required": [
"queueId"
],
"properties": {
"queueId": {
"type": "string"
}
}
},
"result": {
"type": "object",
"additionalProperties": false,
"required": [
"queue",
"startedTaskIds"
],
"properties": {
"queue": {
"$ref": "https://velox.dev/schema/types/Queue.schema.json"
},
"startedTaskIds": {
"type": "array",
"items": {
"type": "string",
"format": "uuid"
}
}
}
}
}
}
@@ -0,0 +1,52 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/methods/queue.stop.schema.json",
"title": "queue.stop",
"description": "Stop admitting new tasks from a queue. Tasks already running are paused when pauseRunning is true, and otherwise allowed to finish \u2014 the difference between 'stop the queue' and 'stop everything', which IDM conflates and users trip over.",
"x-privileged": true,
"x-transports": [
"uds"
],
"x-deadlineMs": 5000,
"x-errors": [
-32003
],
"type": "object",
"properties": {
"params": {
"type": "object",
"additionalProperties": false,
"required": [
"queueId"
],
"properties": {
"queueId": {
"type": "string"
},
"pauseRunning": {
"type": "boolean"
}
}
},
"result": {
"type": "object",
"additionalProperties": false,
"required": [
"queue",
"pausedTaskIds"
],
"properties": {
"queue": {
"$ref": "https://velox.dev/schema/types/Queue.schema.json"
},
"pausedTaskIds": {
"type": "array",
"items": {
"type": "string",
"format": "uuid"
}
}
}
}
}
}
@@ -0,0 +1,41 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/methods/queue.upsert.schema.json",
"title": "queue.upsert",
"description": "Create or replace a queue, including its schedule and concurrency cap. Omit queueId to create. taskIds in the payload is ignored \u2014 membership changes through download.update and queue.reorder so that two clients editing at once cannot silently drop a task.",
"x-privileged": true,
"x-transports": [
"uds"
],
"x-deadlineMs": 5000,
"x-errors": [
-32003
],
"type": "object",
"properties": {
"params": {
"type": "object",
"additionalProperties": false,
"required": [
"queue"
],
"properties": {
"queue": {
"$ref": "https://velox.dev/schema/types/Queue.schema.json"
}
}
},
"result": {
"type": "object",
"additionalProperties": false,
"required": [
"queue"
],
"properties": {
"queue": {
"$ref": "https://velox.dev/schema/types/Queue.schema.json"
}
}
}
}
}
@@ -0,0 +1,37 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/methods/rules.list.schema.json",
"title": "rules.list",
"description": "The rules engine's table, in priority order. Privileged: these are the daemon's routing policy. The extension gets its own narrowed view through capture.getRules instead.",
"x-privileged": true,
"x-transports": [
"uds"
],
"x-deadlineMs": 2000,
"x-errors": [
-32003
],
"type": "object",
"properties": {
"params": {
"type": "object",
"additionalProperties": false,
"properties": {}
},
"result": {
"type": "object",
"additionalProperties": false,
"required": [
"items"
],
"properties": {
"items": {
"type": "array",
"items": {
"$ref": "https://velox.dev/schema/types/Rule.schema.json"
}
}
}
}
}
}
@@ -0,0 +1,57 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/methods/rules.upsert.schema.json",
"title": "rules.upsert",
"description": "Create, replace, or delete rules in one atomic write. 'upsert' carries the rules to store and 'remove' the ruleIds to drop; applying both at once means a reprioritisation never leaves the table in a half-valid state.",
"x-privileged": true,
"x-transports": [
"uds"
],
"x-deadlineMs": 5000,
"x-errors": [
-32003
],
"type": "object",
"properties": {
"params": {
"type": "object",
"additionalProperties": false,
"required": [
"upsert"
],
"properties": {
"upsert": {
"type": "array",
"items": {
"$ref": "https://velox.dev/schema/types/Rule.schema.json"
}
},
"remove": {
"type": [
"array",
"null"
],
"items": {
"type": "string"
}
}
}
},
"result": {
"type": "object",
"additionalProperties": false,
"description": "The full table after the write, in priority order.",
"required": [
"items"
],
"properties": {
"items": {
"type": "array",
"items": {
"$ref": "https://velox.dev/schema/types/Rule.schema.json"
}
}
}
}
}
}
@@ -0,0 +1,64 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/methods/schedule.get.schema.json",
"title": "schedule.get",
"description": "The schedule for one queue, or every schedule when queueId is null. Backs the Scheduler window.",
"x-privileged": true,
"x-transports": [
"uds"
],
"x-deadlineMs": 2000,
"x-errors": [
-32003
],
"type": "object",
"properties": {
"params": {
"type": "object",
"additionalProperties": false,
"properties": {
"queueId": {
"type": [
"string",
"null"
]
}
}
},
"result": {
"type": "object",
"additionalProperties": false,
"required": [
"items"
],
"properties": {
"items": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": false,
"required": [
"queueId",
"schedule"
],
"properties": {
"queueId": {
"type": "string"
},
"schedule": {
"oneOf": [
{
"$ref": "https://velox.dev/schema/types/Schedule.schema.json"
},
{
"type": "null"
}
]
}
}
}
}
}
}
}
}
@@ -0,0 +1,71 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/methods/schedule.set.schema.json",
"title": "schedule.set",
"description": "Set or clear a queue's schedule. A null schedule clears it and leaves the queue under manual control. Times are local wall-clock and are re-evaluated on a DST change rather than being resolved to absolute instants at set time.",
"x-privileged": true,
"x-transports": [
"uds"
],
"x-deadlineMs": 5000,
"x-errors": [
-32003,
-32602
],
"type": "object",
"properties": {
"params": {
"type": "object",
"additionalProperties": false,
"required": [
"queueId",
"schedule"
],
"properties": {
"queueId": {
"type": "string"
},
"schedule": {
"oneOf": [
{
"$ref": "https://velox.dev/schema/types/Schedule.schema.json"
},
{
"type": "null"
}
]
}
}
},
"result": {
"type": "object",
"additionalProperties": false,
"required": [
"queueId",
"schedule"
],
"properties": {
"queueId": {
"type": "string"
},
"schedule": {
"oneOf": [
{
"$ref": "https://velox.dev/schema/types/Schedule.schema.json"
},
{
"type": "null"
}
]
},
"nextRunAt": {
"type": [
"string",
"null"
],
"format": "date-time"
}
}
}
}
}
@@ -0,0 +1,36 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/methods/session.hello.schema.json",
"title": "session.hello",
"description": "First call on every connection, on every transport. The daemon compares protocolVersion majors and refuses a mismatch with -32001 so a stale GUI or extension fails loudly on connect instead of subtly at the tenth field. On the WebSocket transport a valid token is required unless the client is about to call session.pair.",
"x-privileged": false,
"x-transports": ["uds", "ws"],
"x-deadlineMs": 2000,
"x-errors": [-32001, -32002],
"type": "object",
"properties": {
"params": {
"type": "object",
"additionalProperties": false,
"required": ["clientType", "clientName", "protocolVersion"],
"properties": {
"clientType": { "type": "string", "enum": ["gui", "cli", "extension", "nmhost", "test"] },
"clientName": { "type": "string", "maxLength": 64, "description": "Human-readable, shown in the pairing prompt and the logs." },
"protocolVersion": { "type": "string", "pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+(-[0-9A-Za-z.-]+)?$" },
"token": { "type": ["string", "null"], "description": "Required on the WebSocket transport once paired. Ignored on the Unix socket, where SO_PEERCRED is the authorization." }
}
},
"result": {
"type": "object",
"additionalProperties": false,
"required": ["daemonVersion", "protocolVersion", "capabilities", "sessionId"],
"properties": {
"daemonVersion": { "type": "string" },
"protocolVersion": { "type": "string" },
"capabilities": { "type": "array", "items": { "type": "string" }, "description": "Optional features this build has, e.g. 'media', 'grabber', 'secretservice'. A client must degrade gracefully when one is absent rather than assuming it." },
"sessionId": { "type": "string", "format": "uuid" },
"transport": { "type": "string", "enum": ["uds", "ws"], "description": "How the daemon sees this connection. Lets a client know up front which privileged methods will be refused." }
}
}
}
}
@@ -0,0 +1,32 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/methods/session.pair.schema.json",
"title": "session.pair",
"description": "WebSocket transport only. Triggers a GUI or desktop-notification prompt showing a four-digit code; the user must approve before a token is issued. Failed attempts are rate-limited to 5/min followed by a 60 s lockout (-32014) so a token cannot be brute-forced by another local process. The daemon stores only a hash of the token.",
"x-privileged": false,
"x-transports": ["ws"],
"x-deadlineMs": 120000,
"x-errors": [-32003, -32014],
"type": "object",
"properties": {
"params": {
"type": "object",
"additionalProperties": false,
"required": ["clientName", "extensionId"],
"properties": {
"clientName": { "type": "string", "maxLength": 64 },
"extensionId": { "type": "string", "description": "The moz-extension origin UUID. Must match the Origin header verified on the WS upgrade." },
"code": { "type": ["string", "null"], "pattern": "^[0-9]{4}$", "description": "Set when the user typed the code into the extension's Options page instead of clicking Allow in the GUI." }
}
},
"result": {
"type": "object",
"additionalProperties": false,
"required": ["token", "expiresAt"],
"properties": {
"token": { "type": "string", "minLength": 43, "description": "256 bits, base64url. Stored by the extension in browser.storage.local and sent on every later connect." },
"expiresAt": { "type": ["string", "null"], "format": "date-time", "description": "null means the token does not expire; it is revoked from Options -> Unpair." }
}
}
}
}
@@ -0,0 +1,38 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/methods/session.subscribe.schema.json",
"title": "session.subscribe",
"description": "Choose which notifications this connection receives. Subscribing replaces the previous selection rather than adding to it, so a client can narrow its firehose without reconnecting. Nothing is delivered until this is called.",
"x-privileged": false,
"x-transports": ["uds", "ws"],
"x-deadlineMs": 2000,
"type": "object",
"properties": {
"params": {
"type": "object",
"additionalProperties": false,
"required": ["events"],
"properties": {
"events": {
"type": "array",
"items": {
"type": "string",
"enum": ["event.task.added", "event.task.removed", "event.task.state",
"event.task.progress", "event.speed.global", "event.auth.required",
"event.notify", "event.settings.changed", "event.grabber.progress"]
}
},
"taskIds": { "type": ["array", "null"], "items": { "type": "string", "format": "uuid" }, "description": "Narrow task events to these ids. The extension popup uses it to avoid receiving progress for downloads it is not showing. null means all tasks." }
}
},
"result": {
"type": "object",
"additionalProperties": false,
"required": ["ok", "events"],
"properties": {
"ok": { "type": "boolean" },
"events": { "type": "array", "items": { "type": "string" }, "description": "Echoed back so a client can detect that it asked for an event this daemon does not emit." }
}
}
}
}
@@ -0,0 +1,44 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/methods/settings.get.schema.json",
"title": "settings.get",
"description": "Read settings. keys null means everything. Privileged: the settings bag names local filesystem paths and the allowed write roots, which the extension has no business enumerating \u2014 it gets capture.getRules instead.",
"x-privileged": true,
"x-transports": [
"uds"
],
"x-deadlineMs": 2000,
"x-errors": [
-32003
],
"type": "object",
"properties": {
"params": {
"type": "object",
"additionalProperties": false,
"properties": {
"keys": {
"type": [
"array",
"null"
],
"items": {
"$ref": "https://velox.dev/schema/types/SettingKey.schema.json"
}
}
}
},
"result": {
"type": "object",
"additionalProperties": false,
"required": [
"values"
],
"properties": {
"values": {
"$ref": "https://velox.dev/schema/types/Settings.schema.json"
}
}
}
}
}
@@ -0,0 +1,51 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/methods/settings.set.schema.json",
"title": "settings.set",
"description": "Write settings. Only the keys present in values change. Rejected with -32602 if a key is unknown or a value fails the Settings schema, and with -32011 if a directory key names a path that cannot be written. Emits event.settings.changed with exactly the keys that took effect.",
"x-privileged": true,
"x-transports": [
"uds"
],
"x-deadlineMs": 5000,
"x-errors": [
-32003,
-32602,
-32011
],
"type": "object",
"properties": {
"params": {
"type": "object",
"additionalProperties": false,
"required": [
"values"
],
"properties": {
"values": {
"$ref": "https://velox.dev/schema/types/Settings.schema.json"
}
}
},
"result": {
"type": "object",
"additionalProperties": false,
"description": "The stored values for the keys that were set, and the list of keys that actually changed.",
"required": [
"values",
"changed"
],
"properties": {
"values": {
"$ref": "https://velox.dev/schema/types/Settings.schema.json"
},
"changed": {
"type": "array",
"items": {
"$ref": "https://velox.dev/schema/types/SettingKey.schema.json"
}
}
}
}
}
}
@@ -0,0 +1,38 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/types/BulkTaskResult.schema.json",
"title": "BulkTaskResult",
"description": "Result of a state transition applied to many tasks. A bulk call never fails as a whole because one id was bad: the ids that moved come back in 'updated' and the rest are explained in 'failed'. This is what lets the GUI's toolbar act on a multi-selection without pre-validating it.",
"type": "object",
"additionalProperties": false,
"required": ["updated", "failed"],
"properties": {
"updated": {
"type": "array",
"description": "One entry per task that actually changed. A task already in the target state is reported here with changed false rather than as a failure.",
"items": {
"type": "object",
"additionalProperties": false,
"required": ["taskId", "state", "changed"],
"properties": {
"taskId": { "type": "string", "format": "uuid" },
"state": { "$ref": "https://velox.dev/schema/types/TaskState.schema.json" },
"changed": { "type": "boolean" }
}
}
},
"failed": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": false,
"required": ["taskId", "code", "message"],
"properties": {
"taskId": { "type": "string", "format": "uuid" },
"code": { "$ref": "https://velox.dev/schema/types/ErrorCode.schema.json" },
"message": { "type": "string" }
}
}
}
}
}
@@ -0,0 +1,8 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/types/BypassModifier.schema.json",
"title": "BypassModifier",
"description": "The modifier key a user holds to make one click bypass capture and let Firefox download normally. Shared by Settings and CaptureRules so the daemon's setting and the extension's mirror of it are literally the same type.",
"type": "string",
"enum": ["alt", "ctrl", "shift", "none"]
}
@@ -0,0 +1,51 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/types/CaptureRules.schema.json",
"title": "CaptureRules",
"description": "The daemon's capture policy, mirrored into the extension so the two can never disagree about what should be intercepted. The extension refreshes this on connect and on event.settings.changed.",
"type": "object",
"additionalProperties": false,
"required": [
"enabled",
"monitoredExtensions",
"monitoredMimeTypes",
"minSizeBytes",
"excludedHosts",
"rulesVersion"
],
"properties": {
"enabled": {
"type": "boolean"
},
"monitoredExtensions": {
"type": "array",
"items": {
"type": "string"
}
},
"monitoredMimeTypes": {
"type": "array",
"items": {
"type": "string"
}
},
"minSizeBytes": {
"type": "integer",
"minimum": 0
},
"excludedHosts": {
"type": "array",
"items": {
"type": "string"
}
},
"bypassModifier": {
"$ref": "https://velox.dev/schema/types/BypassModifier.schema.json"
},
"rulesVersion": {
"type": "integer",
"minimum": 0,
"description": "Bumped on every change. The extension re-fetches when it sees a higher value."
}
}
}
@@ -0,0 +1,18 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/types/Category.schema.json",
"title": "Category",
"description": "A destination folder plus the extensions that route to it. The extension mirrors the extension lists so its capture decision agrees with the daemon's.",
"type": "object",
"additionalProperties": false,
"required": ["categoryId", "name", "saveDir", "extensions", "builtin"],
"properties": {
"categoryId": { "type": "string" },
"name": { "type": "string", "maxLength": 64 },
"saveDir": { "type": "string" },
"extensions": { "type": "array", "items": { "type": "string", "pattern": "^[A-Za-z0-9][A-Za-z0-9+._-]*$" }, "description": "Without the leading dot, lowercase." },
"mimeTypes": { "type": "array", "items": { "type": "string" } },
"builtin": { "type": "boolean", "description": "Compressed, Documents, Music, Programs, Video. Cannot be removed; can be renamed and re-pointed." },
"sortOrder": { "type": "integer", "minimum": 0 }
}
}
@@ -0,0 +1,13 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/types/Checksum.schema.json",
"title": "Checksum",
"description": "Optional integrity check, verified during the verifying state. A mismatch moves the task to failed and never overwrites a good file.",
"type": "object",
"additionalProperties": false,
"required": ["algorithm", "value"],
"properties": {
"algorithm": { "type": "string", "enum": ["md5", "sha1", "sha256", "sha512"] },
"value": { "type": "string", "pattern": "^[0-9a-fA-F]{32,128}$" }
}
}
+17
View File
@@ -0,0 +1,17 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/types/Cookie.schema.json",
"title": "Cookie",
"description": "One cookie the daemon replays so an authenticated download works outside the browser.",
"type": "object",
"additionalProperties": false,
"required": ["name", "value"],
"properties": {
"name": { "type": "string" },
"value": { "type": "string" },
"domain": { "type": "string" },
"path": { "type": "string" },
"secure": { "type": "boolean" },
"httpOnly": { "type": "boolean" }
}
}
@@ -0,0 +1,25 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/types/DownloadSpec.schema.json",
"title": "DownloadSpec",
"description": "Everything needed to create one task. Shared by download.add and each item of download.addBatch, so the two can never drift apart.",
"type": "object",
"additionalProperties": false,
"required": ["url"],
"properties": {
"url": { "type": "string", "format": "uri" },
"headers": { "oneOf": [{ "$ref": "https://velox.dev/schema/types/Headers.schema.json" }, { "type": "null" }] },
"cookies": { "type": ["array", "null"], "items": { "$ref": "https://velox.dev/schema/types/Cookie.schema.json" } },
"referrer": { "type": ["string", "null"] },
"userAgent": { "type": ["string", "null"] },
"filename": { "type": ["string", "null"], "maxLength": 255, "description": "Overrides the name derived from Content-Disposition or the URL." },
"saveDir": { "type": ["string", "null"], "description": "Canonicalized and checked against the allowed roots before any write. -32011 if it fails." },
"categoryId": { "type": ["string", "null"], "description": "null means the rules engine picks one." },
"queueId": { "type": ["string", "null"], "description": "Required when startMode is 'queue'." },
"segments": { "type": ["integer", "null"], "minimum": 1, "maximum": 32 },
"bufferBytes": { "type": ["integer", "null"], "minimum": 4096, "maximum": 8388608 },
"startMode": { "$ref": "https://velox.dev/schema/types/StartMode.schema.json" },
"description": { "type": ["string", "null"], "maxLength": 1024 },
"checksum": { "oneOf": [{ "$ref": "https://velox.dev/schema/types/Checksum.schema.json" }, { "type": "null" }] }
}
}
@@ -0,0 +1,23 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/types/ErrorCode.schema.json",
"title": "ErrorCode",
"description": "Every error code the daemon may return. Adding one is a minor bump; changing the meaning of one is a major bump.",
"type": "integer",
"x-enum": [
{ "name": "ParseError", "value": -32700, "doc": "Malformed JSON on the wire." },
{ "name": "InvalidRequest", "value": -32600, "doc": "Not a valid JSON-RPC 2.0 request object." },
{ "name": "MethodNotFound", "value": -32601, "doc": "Unknown method name." },
{ "name": "InvalidParams", "value": -32602, "doc": "Params failed schema validation." },
{ "name": "InternalError", "value": -32603, "doc": "Unhandled daemon-side failure." },
{ "name": "VersionMismatch", "value": -32001, "doc": "Protocol major version mismatch. GUI renders this as 'Velox needs updating'." },
{ "name": "NotPaired", "value": -32002, "doc": "Missing or invalid token on the WebSocket transport." },
{ "name": "TransportForbidden", "value": -32003, "doc": "Method is privileged and was called over a transport that may not use it." },
{ "name": "TaskNotFound", "value": -32010, "doc": "No task with that id." },
{ "name": "InvalidPath", "value": -32011, "doc": "Destination is outside the allowed roots, or is not writable. data.path is set." },
{ "name": "DiskFull", "value": -32012, "doc": "Not enough free space to preallocate." },
{ "name": "ProbeFailed", "value": -32013, "doc": "Could not probe the URL. data.httpStatus is set when there was an HTTP response." },
{ "name": "RateLimited", "value": -32014, "doc": "Pairing brute-force lockout. data.retryAfterSec is set." }
],
"enum": [-32700, -32600, -32601, -32602, -32603, -32001, -32002, -32003, -32010, -32011, -32012, -32013, -32014]
}
@@ -0,0 +1,18 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/types/GrabberFile.schema.json",
"title": "GrabberFile",
"description": "One candidate found by the Site Grabber crawl. Nothing is downloaded until grabber.harvest selects it.",
"type": "object",
"additionalProperties": false,
"required": ["fileId", "url", "depth"],
"properties": {
"fileId": { "type": "string" },
"url": { "type": "string", "format": "uri" },
"filename": { "type": ["string", "null"] },
"sizeBytes": { "type": ["integer", "null"], "minimum": 0, "description": "From a HEAD, when the server answered one." },
"contentType": { "type": ["string", "null"] },
"depth": { "type": "integer", "minimum": 0 },
"foundOn": { "type": ["string", "null"], "format": "uri", "description": "The page this link was found on." }
}
}
@@ -0,0 +1,8 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/types/Headers.schema.json",
"title": "Headers",
"description": "HTTP request headers, verbatim as the browser would have sent them. Needed for signed-URL and referrer-gated CDNs.",
"type": "object",
"additionalProperties": { "type": "string" }
}
@@ -0,0 +1,14 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/types/Limiter.schema.json",
"title": "Limiter",
"description": "Global token-bucket speed limit. Applies across every active task, not per task.",
"type": "object",
"additionalProperties": false,
"required": ["enabled", "globalBps"],
"properties": {
"enabled": { "type": "boolean" },
"globalBps": { "type": "integer", "minimum": 0, "description": "Bytes per second. 0 with enabled true means 'stop everything', which the GUI must not offer." },
"applyToRunning": { "type": "boolean", "description": "Re-tune already-running transfers instead of waiting for the next task." }
}
}
@@ -0,0 +1,21 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/types/MediaVariant.schema.json",
"title": "MediaVariant",
"description": "One quality rendition from an HLS or DASH manifest. The daemon parses the manifest; the extension only renders this list. DRM-protected variants are reported with drm true and must be shown greyed out rather than failing later.",
"type": "object",
"additionalProperties": false,
"required": ["variantId", "kind", "drm"],
"properties": {
"variantId": { "type": "string" },
"kind": { "type": "string", "enum": ["video", "audio", "muxed", "subtitle"] },
"resolution": { "type": ["string", "null"], "pattern": "^[0-9]{2,5}x[0-9]{2,5}$" },
"bitrateBps": { "type": ["integer", "null"], "minimum": 0 },
"codec": { "type": ["string", "null"] },
"container": { "type": ["string", "null"], "enum": ["ts", "mp4", "webm", "mkv", null] },
"frameRate": { "type": ["number", "null"], "minimum": 0 },
"language": { "type": ["string", "null"] },
"sizeEstimate": { "type": ["integer", "null"], "minimum": 0, "description": "bitrate x duration. Never exact — the GUI must label it as approximate." },
"drm": { "type": "boolean", "description": "Widevine/EME detected. Explicitly out of scope; refuse rather than fail mysteriously." }
}
}
+18
View File
@@ -0,0 +1,18 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/types/Queue.schema.json",
"title": "Queue",
"description": "An ordered run of tasks with its own concurrency cap and optional schedule.",
"type": "object",
"additionalProperties": false,
"required": ["queueId", "name", "state", "maxConcurrent"],
"properties": {
"queueId": { "type": "string" },
"name": { "type": "string", "maxLength": 64 },
"state": { "type": "string", "enum": ["running", "stopped"] },
"maxConcurrent": { "type": "integer", "minimum": 1, "maximum": 32 },
"taskIds": { "type": "array", "items": { "type": "string", "format": "uuid" }, "description": "In run order. queue.reorder rewrites this." },
"schedule": { "oneOf": [{ "$ref": "https://velox.dev/schema/types/Schedule.schema.json" }, { "type": "null" }] },
"onComplete": { "type": "string", "enum": ["nothing", "exit", "shutdown", "hangup"], "description": "shutdown goes through org.freedesktop.login1 and must be confirmed by the user." }
}
}
+41
View File
@@ -0,0 +1,41 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/types/Rule.schema.json",
"title": "Rule",
"description": "One row of the rules engine: match on extension, MIME, host or size, then route. First match by priority wins; no rule matching means the default category.",
"type": "object",
"additionalProperties": false,
"required": ["ruleId", "enabled", "priority", "match", "action"],
"properties": {
"ruleId": { "type": "string" },
"name": { "type": ["string", "null"], "maxLength": 64 },
"enabled": { "type": "boolean" },
"priority": { "type": "integer", "minimum": 0, "description": "Lower runs first." },
"match": {
"type": "object",
"additionalProperties": false,
"description": "All present clauses must match. An absent clause is not a constraint.",
"properties": {
"extensions": { "type": ["array", "null"], "items": { "type": "string" } },
"mimeTypes": { "type": ["array", "null"], "items": { "type": "string" } },
"hostPattern": { "type": ["string", "null"], "description": "Glob against the effective URL's host, e.g. *.example.com" },
"urlPattern": { "type": ["string", "null"], "description": "Glob against the whole effective URL." },
"minSizeBytes":{ "type": ["integer", "null"], "minimum": 0 },
"maxSizeBytes":{ "type": ["integer", "null"], "minimum": 0 }
}
},
"action": {
"type": "object",
"additionalProperties": false,
"description": "What to do with a matching download.",
"properties": {
"categoryId": { "type": ["string", "null"] },
"saveDir": { "type": ["string", "null"] },
"queueId": { "type": ["string", "null"] },
"segments": { "type": ["integer", "null"], "minimum": 1, "maximum": 32 },
"startMode": { "oneOf": [{ "$ref": "https://velox.dev/schema/types/StartMode.schema.json" }, { "type": "null" }] },
"capture": { "type": ["string", "null"], "enum": ["take", "ignore", null], "description": "Lets a rule veto capture for a host without touching the exclusion list." }
}
}
}
}
@@ -0,0 +1,17 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/types/Schedule.schema.json",
"title": "Schedule",
"description": "When a queue may run. Times are local wall-clock in HH:MM; the daemon re-evaluates them on a DST change rather than caching absolute instants.",
"type": "object",
"additionalProperties": false,
"required": ["enabled", "mode"],
"properties": {
"enabled": { "type": "boolean" },
"mode": { "type": "string", "enum": ["once", "periodic"] },
"startTime": { "type": ["string", "null"], "pattern": "^([01][0-9]|2[0-3]):[0-5][0-9]$" },
"stopTime": { "type": ["string", "null"], "pattern": "^([01][0-9]|2[0-3]):[0-5][0-9]$", "description": "null means run until the queue drains." },
"daysOfWeek": { "type": "array", "items": { "type": "integer", "minimum": 0, "maximum": 6 }, "description": "0 = Sunday. Ignored when mode is 'once'." },
"onceDate": { "type": ["string", "null"], "format": "date", "description": "Set only when mode is 'once'." }
}
}
@@ -0,0 +1,18 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/types/Segment.schema.json",
"title": "Segment",
"description": "One byte range being fetched by one connection. This is the deepest the contract ever exposes the engine: the GUI draws a bar per segment and never learns what a segment steal is.",
"type": "object",
"additionalProperties": false,
"required": ["index", "startByte", "endByte", "downloadedBytes", "state"],
"properties": {
"index": { "type": "integer", "minimum": 0, "maximum": 31 },
"startByte": { "type": "integer", "minimum": 0 },
"endByte": { "type": "integer", "minimum": 0, "description": "Inclusive. Equal to startByte - 1 for an empty segment." },
"downloadedBytes": { "type": "integer", "minimum": 0 },
"speedBps": { "type": "integer", "minimum": 0 },
"state": { "type": "string", "enum": ["pending", "connecting", "receiving", "stalled", "complete", "failed"] },
"httpStatus": { "type": ["integer", "null"] }
}
}
@@ -0,0 +1,29 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/types/SettingKey.schema.json",
"title": "SettingKey",
"description": "Every settings key that exists. The Options dialog maps 1:1 onto this list and the GUI must not invent a key that is not here. Kept in lockstep with Settings.schema.json by a conformance check.",
"type": "string",
"enum": [
"general.launchOnLogin", "general.minimizeToTray", "general.showDropTarget",
"general.confirmOnExit", "general.language", "general.checkForUpdates",
"capture.enabled", "capture.monitoredExtensions", "capture.monitoredMimeTypes",
"capture.minSizeBytes", "capture.excludedHosts", "capture.bypassModifier",
"capture.autoStartTypes",
"saveTo.defaultDir", "saveTo.tempDir", "saveTo.allowedRoots",
"saveTo.fileExistsPolicy", "saveTo.createSubfolderPerSite",
"connection.preset", "connection.maxSegmentsPerDownload", "connection.bufferBytes",
"connection.maxConcurrentDownloads", "connection.timeoutSec", "connection.maxRetries",
"connection.retryBackoffSec",
"downloads.speedLimitBps", "downloads.speedLimitEnabled", "downloads.virusScanCommand",
"downloads.postDownloadCommand", "downloads.duplicatePolicy", "downloads.verifyChecksums",
"proxy.mode", "proxy.host", "proxy.port", "proxy.username", "proxy.bypassHosts", "proxy.pacUrl",
"sounds.enabled", "sounds.onComplete", "sounds.onQueueComplete", "sounds.onError"
]
}
+196
View File
@@ -0,0 +1,196 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/types/Settings.schema.json",
"title": "Settings",
"description": "A sparse bag of settings. Every property is optional because settings.get returns only the keys that were asked for and settings.set carries only the keys that changed. Property names must match SettingKey exactly. NOTE: no password lives here \u2014 proxy and site-login credentials go to the Secret Service, never to SQLite and never over the wire.",
"type": "object",
"additionalProperties": false,
"properties": {
"general.launchOnLogin": {
"type": "boolean"
},
"general.minimizeToTray": {
"type": "boolean"
},
"general.showDropTarget": {
"type": "boolean"
},
"general.confirmOnExit": {
"type": "boolean"
},
"general.language": {
"type": "string",
"description": "BCP 47, or 'system'."
},
"general.checkForUpdates": {
"type": "boolean"
},
"capture.enabled": {
"type": "boolean"
},
"capture.monitoredExtensions": {
"type": "array",
"items": {
"type": "string"
}
},
"capture.monitoredMimeTypes": {
"type": "array",
"items": {
"type": "string"
}
},
"capture.minSizeBytes": {
"type": "integer",
"minimum": 0
},
"capture.excludedHosts": {
"type": "array",
"items": {
"type": "string"
}
},
"capture.bypassModifier": {
"$ref": "https://velox.dev/schema/types/BypassModifier.schema.json"
},
"capture.autoStartTypes": {
"type": "array",
"items": {
"type": "string"
},
"description": "Extensions that skip the File Info dialog and start immediately."
},
"saveTo.defaultDir": {
"type": "string"
},
"saveTo.tempDir": {
"type": "string"
},
"saveTo.allowedRoots": {
"type": "array",
"items": {
"type": "string"
},
"description": "Every write target is canonicalized and must resolve inside one of these. Read-only over the WebSocket transport."
},
"saveTo.fileExistsPolicy": {
"type": "string",
"enum": [
"ask",
"rename",
"overwrite",
"resume"
]
},
"saveTo.createSubfolderPerSite": {
"type": "boolean"
},
"connection.preset": {
"type": "string",
"enum": [
"auto",
"lan",
"broadband",
"slow"
]
},
"connection.maxSegmentsPerDownload": {
"type": "integer",
"minimum": 1,
"maximum": 32
},
"connection.bufferBytes": {
"type": "integer",
"minimum": 4096,
"maximum": 8388608
},
"connection.maxConcurrentDownloads": {
"type": "integer",
"minimum": 1,
"maximum": 64
},
"connection.timeoutSec": {
"type": "integer",
"minimum": 1,
"maximum": 3600
},
"connection.maxRetries": {
"type": "integer",
"minimum": 0,
"maximum": 100
},
"connection.retryBackoffSec": {
"type": "integer",
"minimum": 0,
"maximum": 3600
},
"downloads.speedLimitBps": {
"type": "integer",
"minimum": 0
},
"downloads.speedLimitEnabled": {
"type": "boolean"
},
"downloads.virusScanCommand": {
"type": "string"
},
"downloads.postDownloadCommand": {
"type": "string"
},
"downloads.duplicatePolicy": {
"type": "string",
"enum": [
"ask",
"skip",
"rename",
"redownload"
]
},
"downloads.verifyChecksums": {
"type": "boolean"
},
"proxy.mode": {
"type": "string",
"enum": [
"system",
"none",
"http",
"https",
"socks5",
"pac"
]
},
"proxy.host": {
"type": "string"
},
"proxy.port": {
"type": "integer",
"minimum": 1,
"maximum": 65535
},
"proxy.username": {
"type": "string"
},
"proxy.bypassHosts": {
"type": "array",
"items": {
"type": "string"
}
},
"proxy.pacUrl": {
"type": "string"
},
"sounds.enabled": {
"type": "boolean"
},
"sounds.onComplete": {
"type": "string"
},
"sounds.onQueueComplete": {
"type": "string"
},
"sounds.onError": {
"type": "string"
}
}
}
@@ -0,0 +1,8 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/types/StartMode.schema.json",
"title": "StartMode",
"description": "What the daemon does with a task the moment it is added. 'later' is the File Info dialog's Download Later button and lands the task in paused.",
"type": "string",
"enum": ["now", "later", "queue"]
}
@@ -0,0 +1,27 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/types/TaskDetail.schema.json",
"title": "TaskDetail",
"description": "Everything TaskSummary carries, plus what only the progress dialog and the File Info dialog need. Returned by download.get; never sent in a list or an event, because it is expensive to build.",
"type": "object",
"additionalProperties": false,
"required": ["summary", "segmentDetail"],
"properties": {
"summary": { "$ref": "https://velox.dev/schema/types/TaskSummary.schema.json" },
"segmentDetail": {
"type": "array",
"maxItems": 32,
"items": { "$ref": "https://velox.dev/schema/types/Segment.schema.json" }
},
"headers": { "oneOf": [{ "$ref": "https://velox.dev/schema/types/Headers.schema.json" }, { "type": "null" }] },
"referrer": { "type": ["string", "null"] },
"userAgent": { "type": ["string", "null"] },
"mime": { "type": ["string", "null"] },
"bufferBytes": { "type": ["integer", "null"], "minimum": 4096, "maximum": 8388608 },
"partPath": { "type": ["string", "null"], "description": "Absolute path of the .veloxpart file while the task is unfinished." },
"checksum": { "oneOf": [{ "$ref": "https://velox.dev/schema/types/Checksum.schema.json" }, { "type": "null" }] },
"checksumVerified": { "type": ["boolean", "null"], "description": "null until the verifying state has run." },
"averageSpeedBps": { "type": ["integer", "null"], "minimum": 0 },
"retryCount": { "type": "integer", "minimum": 0 }
}
}
@@ -0,0 +1,17 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/types/TaskError.schema.json",
"title": "TaskError",
"description": "Why a task is in the failed or retry_wait state. Distinct from the JSON-RPC Error, which describes a failed call rather than a failed download.",
"type": "object",
"additionalProperties": false,
"required": ["code", "message", "retryable"],
"properties": {
"code": { "type": "integer" },
"message": { "type": "string" },
"httpStatus": { "type": ["integer", "null"] },
"retryable": { "type": "boolean" },
"attempt": { "type": ["integer", "null"], "minimum": 0, "description": "How many attempts have been made so far." },
"nextRetryAt":{ "type": ["string", "null"], "format": "date-time" }
}
}
@@ -0,0 +1,16 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/types/TaskFilter.schema.json",
"title": "TaskFilter",
"description": "Which rows download.list returns. This is the category tree and the All/Unfinished/Finished nodes, expressed on the wire. Absent clauses are not constraints.",
"type": "object",
"additionalProperties": false,
"properties": {
"states": { "type": ["array", "null"], "items": { "$ref": "https://velox.dev/schema/types/TaskState.schema.json" } },
"categoryId": { "type": ["string", "null"] },
"queueId": { "type": ["string", "null"] },
"query": { "type": ["string", "null"], "maxLength": 256, "description": "Case-insensitive substring of filename or url." },
"addedAfter": { "type": ["string", "null"], "format": "date-time" },
"addedBefore":{ "type": ["string", "null"], "format": "date-time" }
}
}
@@ -0,0 +1,13 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/types/TaskSort.schema.json",
"title": "TaskSort",
"description": "Sort order for download.list. The GUI persists the user's choice and sends it on every list call; the daemon does the sorting so a 100k-row list never has to be materialized client-side.",
"type": "object",
"additionalProperties": false,
"required": ["field", "direction"],
"properties": {
"field": { "type": "string", "enum": ["filename", "sizeBytes", "state", "etaSeconds", "speedBps", "lastTryAt", "createdAt", "queuePosition", "description"] },
"direction": { "type": "string", "enum": ["asc", "desc"] }
}
}
@@ -0,0 +1,9 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/types/TaskState.schema.json",
"title": "TaskState",
"description": "Lifecycle of one download. The daemon is the only writer; clients render it and nothing more. Terminal states are complete, failed and cancelled.",
"type": "string",
"enum": ["new", "probing", "queued", "connecting", "downloading", "paused",
"retry_wait", "assembling", "verifying", "complete", "failed", "cancelled"]
}
+13 -25
View File
@@ -2,43 +2,31 @@
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/types/TaskSummary.schema.json",
"title": "TaskSummary",
"description": "One row of the main download list. Everything the GUI table needs, and nothing more. TEMPLATE — lane PROTO owns the final shape.",
"description": "One row of the main download list. Everything the GUI table needs, and nothing more. TaskDetail is the same shape plus the fields only the progress dialog and File Info dialog need.",
"type": "object",
"additionalProperties": false,
"required": ["taskId", "filename", "state", "createdAt"],
"required": ["taskId", "filename", "saveDir", "url", "state", "downloadedBytes",
"speedBps", "resumable", "segments", "createdAt"],
"properties": {
"taskId": { "type": "string", "format": "uuid" },
"filename": { "type": "string", "maxLength": 255 },
"saveDir": { "type": "string" },
"url": { "type": "string", "format": "uri" },
"effectiveUrl": { "type": "string", "format": "uri" },
"sizeBytes": { "type": ["integer", "null"], "minimum": 0, "description": "null when the server did not report a length" },
"saveDir": { "type": "string", "description": "Absolute, canonicalized, inside an allowed root." },
"url": { "type": "string", "format": "uri", "description": "The URL as the user or the extension supplied it." },
"effectiveUrl": { "type": ["string", "null"], "format": "uri", "description": "After redirects. null until the first probe succeeds." },
"sizeBytes": { "type": ["integer", "null"], "minimum": 0, "description": "null when the server did not report a length." },
"downloadedBytes": { "type": "integer", "minimum": 0 },
"state": {
"type": "string",
"enum": ["new", "probing", "queued", "connecting", "downloading", "paused",
"retry_wait", "assembling", "verifying", "complete", "failed", "cancelled"]
},
"state": { "$ref": "https://velox.dev/schema/types/TaskState.schema.json" },
"speedBps": { "type": "integer", "minimum": 0 },
"etaSeconds": { "type": ["integer", "null"], "minimum": 0 },
"etaSeconds": { "type": ["integer", "null"], "minimum": 0, "description": "null when the size or the speed is unknown." },
"resumable": { "type": "boolean" },
"segments": { "type": "integer", "minimum": 1, "maximum": 32 },
"segments": { "type": "integer", "minimum": 1, "maximum": 32, "description": "Connection count. Per-segment detail lives in TaskDetail." },
"categoryId": { "type": ["string", "null"] },
"queueId": { "type": ["string", "null"] },
"queuePosition":{ "type": ["integer", "null"], "minimum": 0, "description": "the Q column" },
"description": { "type": "string", "maxLength": 1024 },
"queuePosition":{ "type": ["integer", "null"], "minimum": 0, "description": "The Q column." },
"description": { "type": ["string", "null"], "maxLength": 1024 },
"createdAt": { "type": "string", "format": "date-time" },
"lastTryAt": { "type": ["string", "null"], "format": "date-time" },
"completedAt": { "type": ["string", "null"], "format": "date-time" },
"error": {
"type": ["object", "null"],
"additionalProperties": false,
"properties": {
"code": { "type": "integer" },
"message": { "type": "string" },
"httpStatus": { "type": ["integer", "null"] },
"retryable": { "type": "boolean" }
}
}
"error": { "oneOf": [{ "$ref": "https://velox.dev/schema/types/TaskError.schema.json" }, { "type": "null" }] }
}
}