{"id":50841015,"url":"https://github.com/tetherto/miningos-wrk-ork","last_synced_at":"2026-06-14T06:35:11.555Z","repository":{"id":345761871,"uuid":"1129676275","full_name":"tetherto/miningos-wrk-ork","owner":"tetherto","description":null,"archived":false,"fork":false,"pushed_at":"2026-06-08T15:31:41.000Z","size":306,"stargazers_count":2,"open_issues_count":2,"forks_count":7,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-06-08T17:23:06.010Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tetherto.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-01-07T12:28:44.000Z","updated_at":"2026-05-21T12:17:17.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/tetherto/miningos-wrk-ork","commit_stats":null,"previous_names":["tetherto/miningos-wrk-ork"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/tetherto/miningos-wrk-ork","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tetherto%2Fminingos-wrk-ork","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tetherto%2Fminingos-wrk-ork/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tetherto%2Fminingos-wrk-ork/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tetherto%2Fminingos-wrk-ork/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tetherto","download_url":"https://codeload.github.com/tetherto/miningos-wrk-ork/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tetherto%2Fminingos-wrk-ork/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34312072,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-14T02:00:07.365Z","response_time":62,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-06-14T06:35:10.978Z","updated_at":"2026-06-14T06:35:11.550Z","avatar_url":"https://github.com/tetherto.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# miningos-wrk-ork\n\n## Table of Contents\n\n1. [Overview](#overview)\n2. [Architecture](#architecture)\n3. [Dependencies](#dependencies)\n4. [Configuration](#configuration)\n5. [RPC API Reference](#rpc-api-reference)\n6. [Data Aggregation System](#data-aggregation-system)\n7. [Action Management System](#action-management-system)\n8. [Deployment](#deployment)\n9. [Development](#development)\n\n---\n\n## Overview\n\n### Purpose\n\n`miningos-wrk-ork` (Orchestrator Worker) is the **centralized coordination and aggregation layer** for the MiningOS. It serves as the controller that:\n\n- **Manages distributed worker registration** (\"racks\") via Hyperswarm P2P RPC\n- **Aggregates operational data** from miners, containers, sensors, powermeters, and mining pools\n- **Coordinates write operations** through a multi-voter approval system\n- **Provides unified query interface** for the entire mining operation\n- **Stores historical time-series data** with timezone-aware aggregation\n\n### Position in Ecosystem\n\n```\n┌─────────────────────────────────────────────────────┐\n│              miningos-wrk-ork (Orchestrator)           │\n│  • Unified RPC interface                            │\n│  • Action approval \u0026 voting                         │\n│  • Cross-rack data aggregation                      │\n└──────────────────┬──────────────────────────────────┘\n                   │\n        ┌──────────┼──────────┐\n        │          │          │\n        ▼          ▼          ▼\n   ┌────────┐ ┌────────┐ ┌────────┐\n   │ Rack 1 │ │ Rack 2 │ │ Rack N │  (Registered Workers)\n   │ miner  │ │contain-│ │sensor  │\n   │ worker │ │  er    │ │ worker │\n   └────────┘ └────────┘ └────────┘\n        │          │          │\n        ▼          ▼          ▼\n   [Hardware] [Hardware] [Hardware]\n```\n\n**Key Concepts:**\n- **Rack**: A registered worker instance managing a specific type of device (miners, containers, sensors, etc.)\n- **Thing**: An individual device or service managed by a rack\n- **Action**: A write operation requiring approval votes before execution\n- **Aggregation**: Consolidation of metrics across racks with statistical operations\n\n---\n\n## Architecture\n\n### Class Hierarchy\n\n```mermaid\n---\ntitle: Object Model of MiningOS including orchestrator\n---\nflowchart RL\n    bfx-wrk-base@{ shape: stadium, label: \"*bfx-wrk-base*\" }\n\n    tether-wrk-base@{ shape: stadium, label: \"*tether-wrk-base*\" }\n    tether-wrk-base---\u003ebfx-wrk-base\n\n    miningos-wrk-ork@{ label: \"*miningos-wrk-ork*\" }\n    miningos-wrk-ork---\u003etether-wrk-base\n\n\n    style miningos-wrk-ork fill:#005,stroke-width:4px\n```\n\n### Core Components\n\n#### 1. Rack Registry\n- **Storage**: Hyperbee database (`racks`)\n- **Purpose**: Maintains registered worker instances with RPC public keys\n- **Operations**: Register, list, forget racks by type\n\n#### 2. Action Approval System\n- **Facility**: `svc-facs-action-approver`\n- **Storage**: Hyperbee database (`action-approver`)\n- **States**: `voting` → `ready` → `executing` → `done`\n- **Voting**: Configurable positive/negative vote thresholds\n\n#### 3. Aggregation Engine\n- **Library**: `miningos-lib-stats`\n- **Storage**: Hyperbee database (`tail-log-aggr`)\n- **Operations**: sum, avg, obj_concat, arr_concat, alerts_aggr\n- **Time Ranges**: Configurable intervals with timezone support\n\n### Data Flow\n\n```\nRack Workers → RPC → Orchestrator → Aggregation → Storage\n                         ↓\n                  Query Interface\n                         ↓\n                    API Consumers\n```\n\n---\n\n\n## RPC API Reference\n\n### Inherited Methods (from `tether-wrk-base`)\n\n| Method | Operation | Description |\n|--------|-----------|-------------|\n| `ping` | Read | Echo test (returns input) |\n| `getInstanceId` | Read | Returns worker instance UUID |\n\n### Rack Management\n\n#### `registerRack`\n\nRegister a new worker instance.\n\n**Operation:** Write\n\n**Request:**\n```json\n{\n  \"id\": \"miner-wm-m56s-shard-0\",\n  \"type\": \"miner\",\n  \"info\": {\n    \"rpcPublicKey\": \"64-char-hex-public-key\"\n  }\n}\n```\n\n**Response:** `1` (success)\n\n**Errors:**\n- `ERR_RACK_ID_INVALID`\n- `ERR_RACK_TYPE_INVALID`\n- `ERR_RACK_INFO_RPC_PUBKEY_INVALID`\n\n**Example:**\n```bash\nhp-rpc-cli -s wrk-st-0 -m registerRack -d '{\n  \"id\": \"miner-wm-m56s-shard-0\",\n  \"type\": \"miner\",\n  \"info\": {\"rpcPublicKey\": \"abc123...\"}\n}'\n```\n\n#### `listRacks`\n\nList registered racks.\n\n**Operation:** Read\n\n**Request:**\n```json\n{\n  \"type\": \"miner\",  // optional: filter by type prefix\n  \"keys\": 1         // optional: include RPC keys (default: hidden)\n}\n```\n\n**Response:**\n```json\n[\n  {\n    \"id\": \"miner-wm-m56s-shard-0\",\n    \"type\": \"miner\",\n    \"info\": {\n      \"rpcPublicKey\": \"...\"  // only if keys=1\n    }\n  }\n]\n```\n\n**Errors:**\n- `ERR_TYPE_INVALID`\n\n**Example:**\n```bash\nhp-rpc-cli -s wrk-st-0 -m listRacks -d '{\"keys\":1}'\n```\n\n#### `forgetRacks`\n\nRemove registered racks.\n\n**Operation:** Write\n\n**Request:**\n```json\n{\n  \"ids\": [\"rack-id-1\", \"rack-id-2\"],  // specific racks\n  \"all\": true                          // or all racks\n}\n```\n\n**Response:** Number of racks removed\n\n### Thing Management\n\n#### `listThings`\n\nQuery devices across all racks.\n\n**Operation:** Read\n\n**Request:**\n```json\n{\n  \"query\": {                    // MongoDB-style query\n    \"last.snap.stats.status\": {\"$eq\": \"online\"},\n    \"tags\": {\"$in\": [\"t-miner\"]}\n  },\n  \"fields\": {                   // Projection\n    \"id\": 1,\n    \"last.snap.stats.hashrate\": 1\n  },\n  \"sort\": {                     // Sort order\n    \"last.snap.stats.hashrate\": -1\n  },\n  \"limit\": 100\n}\n```\n\n**Response:**\n```json\n[\n  {\n    \"id\": \"miner-001\",\n    \"last\": {\n      \"snap\": {\n        \"stats\": {\n          \"hashrate\": 125000000\n        }\n      }\n    }\n  }\n]\n```\n\n**Notes:**\n- Aggregates results from all registered racks\n- Supports full MongoDB query syntax via `mingo`\n- Parallel queries with limit of 25 concurrent rack requests\n\n#### `forgetThings`\n\nRemove devices from racks.\n\n**Operation:** Write\n\n**Request:**\n```json\n{\n  \"ids\": [\"thing-id-1\", \"thing-id-2\"],\n  \"all\": true  // optional: forget all things\n}\n```\n\n**Response:** Number of things removed across all racks\n\n### Historical Data\n\n#### `getHistoricalLogs`\n\nRetrieve historical log entries.\n\n**Operation:** Read\n\n**Request:**\n```json\n{\n  \"logType\": \"error\",           // log type to retrieve\n  \"query\": {...},               // optional MongoDB query\n  \"sort\": {\"ts\": -1},          // optional sort\n  \"limit\": 100\n}\n```\n\n**Response:** Array of log entries from all racks\n\n#### `tailLog`\n\nQuery aggregated time-series data.\n\n**Operation:** Read\n\n**Request:**\n```json\n{\n  \"type\": \"miner\",              // device type\n  \"key\": \"stat-5m\",             // data key\n  \"tag\": \"t-miner\",             // tag filter\n  \"fields\": {                   // source fields\n    \"hashrate_mhs_1m_sum\": 1\n  },\n  \"aggrFields\": {               // aggregated fields\n    \"hashrate_mhs_1m_sum_aggr\": 1,\n    \"ts\": 1,\n    \"aggrTsRange\": 1,\n    \"aggrCount\": 1,\n    \"aggrIntervals\": 1\n  },\n  \"start\": 1704067200000,       // optional start timestamp\n  \"end\": 1704153600000,         // optional end timestamp\n  \"limit\": 288,                 // max records\n  \"applyAggrCrossthg\": true     // optional: apply cross-thing aggr\n}\n```\n\n**Response:**\n```json\n[\n  {\n    \"ts\": 1704067200000,\n    \"hashrate_mhs_1m_sum_aggr\": 850000000000,\n    \"aggrTsRange\": \"1704067200000-1704070800000\",\n    \"aggrCount\": 25,\n    \"aggrIntervals\": 1\n  }\n]\n```\n\n**Errors:**\n- `ERR_NOT_CONFIGURED`\n- `ERR_TYPE_INVALID`\n- `ERR_TYPE_AGGR_INVALID`\n\n**Notes:**\n- Automatically adds `ts`, `aggrTsRange`, `aggrCount`, `aggrIntervals` to aggregated fields\n- Operations defined in `aggr.stats.json`\n- Supports custom time ranges with `start`/`end`\n\n#### `tailLogMulti`\n\nQuery multiple aggregation types in parallel.\n\n**Operation:** Read\n\n**Request:**\n```json\n{\n  \"keys\": [\n    {\"type\": \"miner\", \"key\": \"stat-5m\", \"tag\": \"t-miner\"},\n    {\"type\": \"container\", \"key\": \"stat-5m\", \"tag\": \"t-container\"}\n  ],\n  \"fields\": {...},              // applied to all\n  \"aggrFields\": {...}           // applied to all\n}\n```\n\n**Response:** Array of results (one per key)\n\n**Notes:**\n- Parallel execution with limit of 5 concurrent queries\n- Each query must have `type` specified\n\n#### `tailLogCustomRangeAggr`\n\nRetrieve pre-aggregated daily data within a date range.\n\n**Operation:** Read\n\n**Request:**\n```json\n{\n  \"keys\": [\n    {\n      \"type\": \"miner\",\n      \"startDate\": \"2024-01-01\",\n      \"endDate\": \"2024-01-31\",\n      \"timezoneOffset\": 0,          // milliseconds\n      \"fields\": {\n        \"hashrate_mhs_5m_avg_aggr\": 1\n      },\n      \"shouldReturnDailyData\": false  // true: daily array, false: range aggr\n    }\n  ]\n}\n```\n\n**Response:**\n```json\n[\n  {\n    \"type\": \"miner\",\n    \"data\": {\n      \"hashrate_mhs_5m_avg_aggr\": 123456789,\n      \"aggrCount\": 8640,              // total snapshots\n      \"aggrIntervals\": 8640           // total intervals\n    },\n    \"error\": null\n  }\n]\n```\n\n**With `shouldReturnDailyData: true`:**\n```json\n[\n  {\n    \"type\": \"miner\",\n    \"data\": [\n      {\"ts\": 1704067200000, \"val\": {...}},\n      {\"ts\": 1704153600000, \"val\": {...}}\n    ],\n    \"error\": null\n  }\n]\n```\n\n**Notes:**\n- Uses pre-stored daily aggregations from `tail-log-aggr` database\n- Requires `global.config.json` with timezone definitions\n- Timezone offset in milliseconds (e.g., UTC-3 = -10800000)\n\n### Thing Comments\n\n#### `saveThingComment`\n\nAdd a comment to a thing.\n\n**Operation:** Write\n\n**Request:**\n```json\n{\n  \"rackId\": \"miner-rack-0\",\n  \"thingId\": \"miner-001\",\n  \"comment\": \"Replaced heat sink\",\n  \"user\": \"technician@example.com\"\n}\n```\n\n**Response:** `1` (success) or `0` (failure)\n\n#### `editThingComment`\n\nEdit an existing comment.\n\n**Operation:** Write\n\n**Request:**\n```json\n{\n  \"rackId\": \"miner-rack-0\",\n  \"thingId\": \"miner-001\",\n  \"commentId\": \"comment-uuid\",\n  \"comment\": \"Updated comment text\"\n}\n```\n\n**Response:** `1` (success) or `0` (failure)\n\n#### `deleteThingComment`\n\nDelete a comment.\n\n**Operation:** Write\n\n**Request:**\n```json\n{\n  \"rackId\": \"miner-rack-0\",\n  \"thingId\": \"miner-001\",\n  \"commentId\": \"comment-uuid\"\n}\n```\n\n**Response:** `1` (success) or `0` (failure)\n\n### Action Management\n\n#### `pushAction`\n\nSubmit an action for approval.\n\n**Operation:** Write\n\n**Request:**\n```json\n{\n  \"query\": {                        // Target selection\n    \"tags\": {\"$in\": [\"t-miner\"]},\n    \"last.snap.stats.status\": {\"$eq\": \"online\"}\n  },\n  \"action\": \"reboot\",               // Action name\n  \"params\": [],                     // Action parameters\n  \"voter\": \"admin@example.com\",     // Initial voter\n  \"authPerms\": [\"miner:rw\"]         // Required permissions\n}\n```\n\n**Response:**\n```json\n{\n  \"id\": 1692277886841,              // Action ID (null if failed)\n  \"data\": {                         // Full action object (when successful)\n    \"id\": 1692277886841,\n    \"action\": \"reboot\",\n    \"params\": [...],\n    \"targets\": {...},\n    \"requiredPerms\": [\"miner\"],\n    \"votes\": {...},\n    \"state\": \"voting\"\n  },\n  \"errors\": []                      // Array of error messages\n}\n```\n\n**Errors (Thrown - stop execution):**\n\n| Error | Condition |\n|-------|-----------|\n| `ERR_QUERY_INVALID` | Query is not a plain object or contains invalid MongoDB syntax |\n| `ERR_ACTION_INVALID` | Action is not a string or is empty |\n| `ERR_PARAMS_INVALID` | Params is not an array |\n| `ERR_ACTION_INVALID_MISSING_RACKID` | Rack actions (`registerThing`, `updateThing`, `forgetThings`) missing `rackId` in params[0] |\n| `ERR_ACTION_INVALID_MISSING_ID` | `updateThing` action missing `id` in params[0] |\n| `ERR_ACTION_INVALID_QUERY_ID` | `forgetThings` action has non-string `query.id` in params[0] |\n\n**Errors (Returned - action partially succeeds):**\n\n| Error | Condition |\n|-------|-----------|\n| `ERR_ORK_ACTION_CALLS_EMPTY` | No targets found matching query and permissions |\n| Rack-specific errors | Individual rack RPC failures (timeout, connection refused, etc.) |\n\n**Notes:**\n- When `ERR_ORK_ACTION_CALLS_EMPTY` occurs, `id` is `null` and no action is created\n- Rack-level errors are non-fatal: action proceeds if at least one rack succeeds\n- All racks are queried in parallel with limit of 5 concurrent requests\n- Rack RPC timeout: 120 seconds\n\n#### `pushActionsBatch`\n\nSubmit multiple actions as a batch.\n\n**Operation:** Write\n\n**Request:**\n```json\n{\n  \"batchActionsPayload\": [\n    {\n      \"query\": {...},\n      \"action\": \"reboot\",\n      \"params\": []\n    },\n    {\n      \"query\": {...},\n      \"action\": \"setPowerMode\",\n      \"params\": [\"sleep\"]\n    }\n  ],\n  \"voter\": \"admin@example.com\",\n  \"authPerms\": [\"miner:rw\"],\n  \"batchActionUID\": \"maintenance-001\"  // optional identifier\n}\n```\n\n**Response:** Array of action IDs and data (one per batch item)\n\n**Errors:**\n\n- `ERR_PAYLOAD_INVALID`\n\n**Notes:**\n- Executes up to 5 actions in parallel\n- Each action gets timestamp-prefixed UID: `{timestamp}-{batchActionUID}`\n\n#### `voteAction`\n\nCast a vote on a pending action.\n\n**Operation:** Write\n\n**Request:**\n```json\n{\n  \"id\": 1692277886841,\n  \"voter\": \"supervisor@example.com\",\n  \"approve\": true,                   // true=approve, false=deny\n  \"authPerms\": [\"miner:rw\"]\n}\n```\n\n**Response:** `1` (success)\n\n**Errors:**\n- `ERR_ACTION_DENIED` (insufficient permissions)\n\n**Example:**\n```bash\nhp-rpc-cli -s wrk-st-0 -m voteAction -d '{\n  \"id\": 1692890293574,\n  \"voter\": \"supervisor@example.com\",\n  \"approve\": true,\n  \"authPerms\": [\"miner\", \"container\"]\n}'\n```\n\n#### `getAction`\n\nRetrieve a specific action.\n\n**Operation:** Read\n\n**Request:**\n```json\n{\n  \"id\": 1692277886841,\n  \"type\": \"voting\"                   // voting/ready/executing/done\n}\n```\n\n**Response:**\n```json\n{\n  \"id\": 1692277886841,\n  \"action\": \"reboot\",\n  \"params\": [],\n  \"targets\": {...},\n  \"requiredPerms\": [\"miner\"],\n  \"votes\": {\n    \"admin@example.com\": true,\n    \"supervisor@example.com\": true\n  },\n  \"reqVotesPos\": 2,\n  \"reqVotesNeg\": 1,\n  \"state\": \"ready\"\n}\n```\n\n#### `getActionsBatch`\n\nRetrieve multiple actions by IDs.\n\n**Operation:** Read\n\n**Request:**\n```json\n{\n  \"ids\": [1692277886841, 1692277999999]\n}\n```\n\n**Response:**\n```json\n[\n  {\n    \"type\": \"done\",\n    \"action\": {...}\n  },\n  {\n    \"type\": \"voting\",\n    \"action\": {...}\n  }\n]\n```\n\n**Notes:**\n- Searches all state databases (voting, ready, executing, done)\n- Returns `null` for IDs not found\n\n#### `queryActions`\n\nQuery actions with filters.\n\n**Operation:** Read\n\n**Request:**\n```json\n{\n  \"queries\": [\n    {\n      \"type\": \"voting\",              // required\n      \"filter\": {                    // timestamp range\n        \"gte\": 1692277886841,\n        \"lte\": 1692364286841\n      },\n      \"opts\": {\n        \"limit\": 100\n      },\n      \"query\": {                     // optional MongoDB query\n        \"action\": {\"$eq\": \"reboot\"}\n      },\n      \"fields\": {                    // optional projection\n        \"id\": 1,\n        \"action\": 1\n      }\n    }\n  ],\n  \"groupBatch\": false                // true: group by batchActionUID\n}\n```\n\n**Response:**\n```json\n{\n  \"voting\": [\n    {\n      \"id\": 1692277886841,\n      \"action\": \"reboot\",\n      \"targets\": {...},\n      \"votes\": {...}\n    }\n  ]\n}\n```\n\n**With `groupBatch: true`:**\n```json\n{\n  \"voting\": [\n    {\n      \"batchActionUID\": \"1692277886841-maintenance-001\",\n      \"id\": \"1692277886841\",\n      \"actions\": [\n        {...},\n        {...}\n      ]\n    }\n  ]\n}\n```\n\n**Filter Options:**\n- `gt`: Greater than timestamp\n- `gte`: Greater than or equal\n- `lt`: Less than timestamp\n- `lte`: Less than or equal\n\n#### `cancelActionsBatch`\n\nCancel multiple actions.\n\n**Operation:** Write\n\n**Request:**\n```json\n{\n  \"ids\": [1692277886841, 1692277999999],\n  \"voter\": \"admin@example.com\"\n}\n```\n\n**Response:** Number of actions cancelled\n\n### Configuration\n\n#### `getGlobalConfig`\n\nRetrieve global site configuration.\n\n**Operation:** Read\n\n**Request:**\n```json\n{\n  \"fields\": {                        // optional projection\n    \"nominalSiteHashrate_MHS\": 1,\n    \"isAutoSleepAllowed\": 1\n  }\n}\n```\n\n**Response:**\n```json\n{\n  \"nominalSiteHashrate_MHS\": 709032000000,\n  \"isAutoSleepAllowed\": false\n}\n```\n\n#### `setGlobalConfig`\n\nUpdate global site configuration.\n\n**Operation:** Write\n\n**Request:**\n```json\n{\n  \"isAutoSleepAllowed\": true\n}\n```\n\n**Response:** Updated global config object or `null`\n\n**Notes:**\n- Currently only supports updating `isAutoSleepAllowed`\n- Modifies in-memory configuration (not persisted to file)\n\n#### `getWrkConf`\n\nRetrieve worker configuration from racks.\n\n**Operation:** Read\n\n**Request:**\n```json\n{\n  \"type\": \"miner\"                    // device type\n}\n```\n\n**Response:**\n```json\n[\n  {\n    \"rackId\": \"miner-rack-0\",\n    \"config\": {...}                  // rack-specific config\n  }\n]\n```\n\n#### `getThingConf`\n\nRetrieve thing configuration from racks.\n\n**Operation:** Read\n\n**Request:**\n```json\n{\n  \"type\": \"miner\"\n}\n```\n\n**Response:**\n```json\n[\n  {\n    \"rackId\": \"miner-rack-0\",\n    \"requestValue\": {...}            // thing-specific config\n  }\n]\n```\n\n#### `getWrkSettings`\n\nRetrieve worker settings from specific rack.\n\n**Operation:** Read\n\n**Request:**\n```json\n{\n  \"rackId\": \"miner-rack-0\"\n}\n```\n\n**Response:** Worker settings object\n\n**Errors:**\n- `ERR_RACK_ID_INVALID`\n- `ERR_GET_SETTINGS_FAILED`\n\n#### `saveWrkSettings`\n\nSave worker settings to specific rack.\n\n**Operation:** Write\n\n**Request:**\n```json\n{\n  \"rackId\": \"miner-rack-0\",\n  \"entries\": {\n    \"key1\": \"value1\",\n    \"key2\": \"value2\"\n  }\n}\n```\n\n**Response:** `1` (success)\n\n**Errors:**\n- `ERR_RACK_ID_INVALID`\n- `ERR_ENTRIES_INVALID`\n- `ERR_SAVE_SETTINGS_FAILED`\n\n\n## Data Aggregation System\n\n### Overview\n\nThe orchestrator aggregates data from distributed racks using a two-tier system:\n\n1. **Real-time Aggregation** (`tailLog`): On-demand aggregation of current data\n2. **Historical Storage** (`tail-log-aggr` DB): Pre-computed daily aggregations\n\n\n### Time-Series Data Flow\n\n```\n1. Racks collect device snapshots (5m intervals)\n       ↓\n2. Orchestrator queries racks via tailLog RPC\n       ↓\n3. Apply aggregation operations (sum/avg/concat)\n       ↓\n4. Return consolidated metrics\n       ↓\n5. Background: Store daily aggregates to DB\n       ↓\n6. API: Query historical ranges via tailLogCustomRangeAggr\n```\n\n## Action Management System\n\n### Action Lifecycle\n\n```\n1. SUBMISSION\n   User/API calls pushAction\n   → Query resolves to target things\n   → Create action with reqVotesPos/reqVotesNeg\n   → State: \"voting\"\n\n2. VOTING\n   Users call voteAction\n   → Permissions checked against requiredPerms\n   → Vote recorded\n   → If threshold met → State: \"ready\"\n   → If denied → State: \"cancelled\"\n\n3. EXECUTION\n   Background worker (actionIntvlMs interval)\n   → Picks up \"ready\" actions\n   → State: \"executing\"\n   → Calls action on all targets via ActionCaller\n   → Records results/errors per target\n\n4. COMPLETION\n   → State: \"done\"\n   → Results available via getAction/queryActions\n```\n\n### Permission System\n\n**Format:** `{type}:{level}`\n\n**Levels:**\n- `r`: Read-only\n- `w`: Write-only\n- `rw`: Read-write\n\n**Examples:**\n- `miner:rw`: Full access to miner actions\n- `container:r`: Read-only container access\n- `miner:w`: Write-only miner access (can execute actions, not query)\n\n**Permission Checking:**\n```javascript\n// User has [\"miner:rw\", \"container:r\"]\n// Action requires [\"miner\"]\nhasPermission([\"miner:rw\", \"container:r\"], \"miner:w\") // true\nhasPermission([\"miner:rw\", \"container:r\"], \"container:w\") // false\n```\n\n### Vote Requirements\n\n**Configuration:**\n```javascript\n{\n  reqVotesPos: 2,  // Requires 2 positive votes to approve\n  reqVotesNeg: 1   // 1 negative vote cancels action\n}\n```\n\n**Logic:**\n- Positive votes accumulate until `reqVotesPos` reached → action ready\n- Any negative vote → action cancelled\n- Racks can specify different vote requirements (highest used)\n\n### Batch Actions\n\n**Features:**\n- Submit multiple actions with shared UID\n- Query/display actions as grouped batch\n- Cancel entire batch with one call\n\n**Batch UID Format:** `{timestamp}-{user-provided-id}`\n\n**Query Grouping:**\n```javascript\nqueryActions({\n  queries: [{type: \"done\", ...}],\n  groupBatch: true  // Groups actions by batchActionUID\n})\n```\n\n### Action Caller System\n\n**Class:** `ActionCaller` (proxied for dynamic action methods)\n\n**Process:**\n1. `getWriteCalls(query, action, params, permissions)`\n   - Queries all racks for matching targets\n   - Checks user permissions\n   - Returns `{targets, requiredPerms}`\n\n2. `callTargets(action, params, targets)`\n   - Executes action on all target things\n   - Parallel execution (limit: `callTargetsLimit` from config)\n   - Records `result` or `error` per target call\n\n**Rack Actions:**\nSpecial actions that operate on rack level (not individual things):\n- `registerThing`\n- `updateThing`\n- `forgetThings`\n- `rackReboot`\n\n## Deployment\n\n### Requirements\n\n- Node.js \u003e= 20.0\n\n### Installation\n\n```bash\n# Clone repository\ngit clone https://github.com/tetherto/miningos-wrk-ork.git\ncd miningos-wrk-ork\n\n# Install dependencies\nnpm install\n\n# Setup configuration files\n./setup-config.sh\n\n```\n\n### Configuration Steps\n\n1. **Network Configuration** (`config/facs/net.config.json`)\n   - Add allowed RPC public keys to `allow` array\n   - Add read-only keys to `allowReadOnly` array\n   - Set `allowLocal: true` for access from same server\n\n2. **Orchestrator Settings** (`config/base.ork.json`)\n   - Adjust `actionIntvlMs` for action processing speed\n   - Set `callTargetsLimit` based on infrastructure size\n   - Configure `automationTypes` for automated actions\n\n3. **Global Configuration** (`config/global.config.json`)\n   - Define timezones for your operation\n   - Set nominal site hashrate and capacity\n   - Configure power consumption thresholds\n   - Enable/disable auto-sleep: `isAutoSleepAllowed`\n\n### Running the Worker\n\n```bash\n# Development\nDEBUG=\"*\" node worker.js --wtype wrk-ork-proc-aggr --env development --cluster shard-1\n\n# Production\nnode worker.js --wtype wrk-ork-proc-aggr --env production --cluster shard-1\n```\n\n**Required Arguments:**\n- `--wtype`: Worker type (must be `wrk-ork-proc-aggr`)\n- `--env`: Environment (development/production)\n- `--cluster`: Cluster identifier (e.g., `shard-1`)\n\n**Notes:**\n- Each orchestrator instance manages its own cluster of racks\n- Use `--cluster` to identify different shards\n- Racks register to specific orchestrator via RPC public key\n\n### Status Files\n\nWorker status saved to: `status/wrk-ork-proc-aggr-{cluster}.json`\n\n```json\n{\n  \"instanceId\": \"wrk-ork-proc-aggr-shard-1-uuid\",\n  \"rpcPublicKey\": \"64-char-hex\",\n  \"rpcClientKey\": \"64-char-hex\"\n}\n```\n\n### Database Storage\n\nHyperbee databases created in: `store/{cluster}-db/`\n\n| Database | Purpose |\n|----------|---------|\n| `racks` | Registered rack registry |\n| `action-approver` | Action states (voting/ready/executing/done) |\n| `tail-log-aggr` | Historical daily aggregations |\n\n---\n\n## Development\n\n### Testing\n\n```bash\n# Run tests\nnpm test\n\n# Lint code\nnpm run lint\n\n# Fix linting issues\nnpm run lint:fix\n```\n\n### Debugging\n\n**Enable debug output:**\n```bash\nDEBUG=\"ork:*\" node worker.js --wtype wrk-ork-proc-aggr --env development --cluster shard-1\n```\n\n**Debug specific methods:**\n```javascript\nthis.debugGeneric('method name', data)  // General logging\nthis.debugError('error context', error, true)  // Error with alert\n```\n\n**Check worker status:**\n```bash\ncat status/wrk-ork-proc-aggr-shard-1.json\n```\n---\n\n## Appendix\n\n### Error Codes Reference\n\n| Error | Meaning |\n|-------|---------|\n| `ERR_PROC_RACK_UNDEFINED` | Cluster argument missing |\n| `ERR_RACK_ID_INVALID` | Invalid or missing rack ID |\n| `ERR_RACK_TYPE_INVALID` | Invalid or missing rack type |\n| `ERR_RACK_INFO_RPC_PUBKEY_INVALID` | Missing RPC public key |\n| `ERR_TYPE_INVALID` | Invalid or missing device type |\n| `ERR_LOG_TYPE_INVALID` | Invalid log type |\n| `ERR_QUERY_INVALID` | Invalid MongoDB query |\n| `ERR_ACTION_INVALID` | Invalid action name |\n| `ERR_PARAMS_INVALID` | Invalid action parameters |\n| `ERR_KEYS_INVALID` | Invalid keys array |\n| `ERR_TYPE_AGGR_INVALID` | Aggregation not configured for type |\n| `ERR_START_DATE_INVALID` | Invalid start date format |\n| `ERR_END_DATE_INVALID` | Invalid end date format |\n| `ERR_TIMEZONE_OFFSET_INVALID` | Timezone not configured |\n| `ERR_NOT_CONFIGURED` | Required config file missing |\n| `ERR_TIMEZONES_NOT_CONFIGURED` | Global timezones missing |\n| `ERR_GLOBAL_CONFIG_MISSING` | Global config not loaded |\n| `ERR_GLOBAL_CONFIG_NOT_FOUND` | Global config not found |\n| `ERR_CONFIG_INVALID` | Invalid config parameters |\n| `ERR_PAYLOAD_INVALID` | Invalid batch payload |\n| `ERR_QUERIES_INVALID` | Invalid queries array |\n| `ERR_QUERIES_TYPE_INVALID` | Query missing type |\n| `ERR_ORK_ACTION_CALLS_EMPTY` | No targets matched query |\n| `ERR_ACTION_DENIED` | Insufficient permissions |\n| `ERR_ACTION_INVALID_MISSING_RACKID` | Rack action missing rackId |\n| `ERR_ACTION_INVALID_MISSING_ID` | Update action missing ID |\n| `ERR_ACTION_INVALID_QUERY_ID` | Forget action missing query.id |\n| `ERR_MISSING_WRITE_PERMISSIONS` | Read-only peer attempted write |\n| `ERR_NET_INVALID_INSTANCE` | Invalid network facility |\n| `ERR_RACKS_INSTANCE_INVALID` | Invalid racks database |\n| `ERR_ENTRIES_INVALID` | Settings entries invalid |\n| `ERR_GET_SETTINGS_FAILED` | Failed to get settings from rack |\n| `ERR_SAVE_SETTINGS_FAILED` | Failed to save settings to rack |\n\n\n### Timezone Configuration\n\n**Common Timezone Offsets:**\n\n| Timezone | Offset (ms) | UTC Offset |\n|----------|-------------|------------|\n| UTC | 0 | UTC+0 |\n| EST | -18000000 | UTC-5 |\n| CST | -21600000 | UTC-6 |\n| MST | -25200000 | UTC-7 |\n| PST | -28800000 | UTC-8 |\n| CET | 3600000 | UTC+1 |\n| EET | 7200000 | UTC+2 |\n| UYT | -10800000 | UTC-3 |\n| JST | 32400000 | UTC+9 |\n\n**Adding Timezone:**\n```json\n{\n  \"aggrTailLogTimezones\": [\n    {\n      \"code\": \"PST\",\n      \"offset\": -28800000\n    }\n  ]\n}\n```\n\n---\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftetherto%2Fminingos-wrk-ork","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftetherto%2Fminingos-wrk-ork","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftetherto%2Fminingos-wrk-ork/lists"}