test(fixtures): capture redacted SimAdmin responses
This commit is contained in:
@@ -0,0 +1,23 @@
|
||||
# Read-only fixture policy (Phase 0.3)
|
||||
|
||||
Fixtures are schema-preserving, redacted responses from exactly two locally configured, password-free SimAdmin instances at upstream baseline `58e2204`. Instance identities and origins are never logged or persisted.
|
||||
|
||||
## Selection and transport
|
||||
|
||||
The OperationRegistry is the sole operation source. Collector initialization fingerprints every complete operation contract. Runtime validation requires the exact Registry object (not a look-alike), an unchanged structural fingerprint, `GET`, `R0`, and a fully bound path beginning with one `/api/`; it rejects absolute/scheme-relative URLs, query/hash, backslashes, duplicate slashes, dot segments, percent encoding, and single/double-decode changes. Selection and `collectOne` both enforce this policy before transport.
|
||||
|
||||
Configuration is checked before normalization (so query/fragment stripping cannot hide an unsafe input), then passed through production `normalizeConfig`; collector-specific constraints require exactly two `http(s)` RFC1918 private-LAN IPv4 origins, no credentials, path, query, fragment, or authentication. `collectOne` repeats origin validation and verifies the final URL retains the exact origin and Registry path. Requests omit credentials, body, Authorization, and Cookie; redirects are manual, timeout is six seconds, and concurrency is two.
|
||||
|
||||
Explicit denials are machine-readable in `DENY_REASONS`: operator scan and `/api/connectivity` are active operations (connectivity performs a ping); content/history/log/query endpoints are omitted where no Registry-backed privacy-safe bounded contract exists. No authentication retry occurs.
|
||||
|
||||
## Data handling and schema preservation
|
||||
|
||||
Raw responses exist only in memory. Before writing, every OperationRegistry `sensitiveFields` entry whose direction is `response` is executed as its endpoint-specific JSONPath; missing optional paths are safe no-ops and unsupported path syntax fails closed. Wildcard arrays are supported. Sensitive objects retain keys, arrays retain length and element shape, and primitive leaves retain their JSON types: strings become `[REDACTED]`, numbers `0`, booleans `false`, and null remains null. Objects and arrays are never stringified into placeholders.
|
||||
|
||||
A recursive fallback sanitizer then covers credentials, access/device/private keys, PIN/PUK, subscriber/device/network identifiers, usernames/accounts, serials/revisions, filesystem/object paths, templates/config, URLs/hosts, IPv4/IPv6, phone-like and separated modem identifiers, and unknown high-entropy secrets. JSON encoded inside strings is parsed, recursively sanitized, and serialized back as a string. Unparseable templates are conservatively replaced. GPS/cell identifiers and device fingerprints (revision, uptime, traffic, precise timestamps) are type-preservingly redacted. Ordinary API `message` status semantics and manual-entry booleans are retained unless an operation path declares them sensitive; SMS/call content remains denied or explicitly redacted.
|
||||
|
||||
## Manifest and verification
|
||||
|
||||
Real and synthetic fixtures are strictly separated; synthetic fixtures, if introduced, must be under `synthetic-errors/`, set `synthetic: true`, and never contribute to real coverage. The manifest file set must exactly equal disk contents, with unique paths and operation/alias pairs, exact domain counts, SHA-256 and sizes. The separately reviewed `response-shapes-58e2204.json` records value-free recursive payload signatures (object keys, every array element, and primitive types) for exactly all 78 alias/operation pairs. It is excluded from `manifest.files` to avoid a cycle but pinned by the manifest `shapeBaseline` size and SHA-256. Capture requires and attests it but never creates or overwrites it. Tests strictly validate its schema and compare every payload to it. Envelopes enforce allowed aliases, date-only reasonable `capturedAt`, Registry correspondence, and category-specific HTTP/content-type/payload consistency.
|
||||
|
||||
Tests recursively collect every configured string leaf of length at least three (including tags, capabilities, auth, and password) and assert none occurs in fixtures without printing values. Credential scans report counts only. Capture logs are limited to alias, operation ID, and result category.
|
||||
@@ -0,0 +1,40 @@
|
||||
import { readFile, mkdir, writeFile, rm, readdir, stat } from 'node:fs/promises';
|
||||
import { createHash } from 'node:crypto';
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { upstream58e2204Operations } from '../../operation-registry/src/upstream-58e2204.ts';
|
||||
import { normalizeConfig } from '../../../server/config/schema.js';
|
||||
import { collectOne, selectReadonlyOperations, validateInstanceOrigin } from './collector.ts';
|
||||
|
||||
const here=path.dirname(fileURLToPath(import.meta.url));const pkg=path.resolve(here,'..');const repo=path.resolve(pkg,'../..');
|
||||
export function loadInstances(config:any){
|
||||
if(!config||!Array.isArray(config.instances)||config.instances.length!==2)throw new Error('config must contain exactly two instances');
|
||||
for(const raw of config.instances){
|
||||
if(!raw||typeof raw!=='object'||typeof raw.url!=='string')throw new Error('invalid collector instance configuration');
|
||||
validateInstanceOrigin(raw.url);
|
||||
if(raw.password||raw.auth?.password||!['none',undefined].includes(raw.auth?.mode))throw new Error('collector instances must be password-free');
|
||||
}
|
||||
const normalized=normalizeConfig(config,{HOST:'127.0.0.1',PORT:'8788'});
|
||||
if(normalized.instances.length!==2)throw new Error('config must contain exactly two instances');
|
||||
return normalized.instances.map((raw:any,i:number)=>{
|
||||
if(raw.auth.mode!=='none'||raw.auth.password)throw new Error(`instance-${i+1} must be password-free`);
|
||||
return {origin:validateInstanceOrigin(raw.url),alias:`instance-${i+1}`};
|
||||
});
|
||||
}
|
||||
async function mapLimit<T,R>(xs:T[],limit:number,fn:(x:T)=>Promise<R>){const out:R[]=[];let n=0;async function worker(){while(n<xs.length){const i=n++;out[i]=await fn(xs[i]);}}await Promise.all(Array.from({length:Math.min(limit,xs.length)},worker));return out;}
|
||||
async function allJson(dir:string):Promise<string[]>{const out:string[]=[];for(const e of await readdir(dir,{withFileTypes:true})){const p=path.join(dir,e.name);if(e.isDirectory())out.push(...await allJson(p));else if(e.name.endsWith('.json'))out.push(p);}return out;}
|
||||
function safeName(s:string){return s.replace(/[^a-zA-Z0-9_-]/g,'-');}
|
||||
export async function main(argv=process.argv.slice(2)){
|
||||
if(argv.length!==1||!['--dry-run','--capture'].includes(argv[0]))throw new Error('usage: collect-readonly-fixtures.ts --dry-run|--capture');
|
||||
const config=JSON.parse(await readFile(path.join(repo,'config.json'),'utf8'));const instances=loadInstances(config);const {selected,denied}=selectReadonlyOperations(upstream58e2204Operations);
|
||||
if(argv[0]==='--dry-run'){for(const op of selected)console.log(`registry ${op.operationId}`);console.log(`selected=${selected.length} denied=${denied.length}`);return;}
|
||||
const shapePath=path.join(pkg,'src/response-shapes-58e2204.json');
|
||||
// Separately reviewed: capture attests this baseline but never creates it.
|
||||
const shapeBytes=await readFile(shapePath);
|
||||
const root=path.join(pkg,'src/simadmin');await rm(root,{recursive:true,force:true});await rm(path.join(pkg,'src/manifest.json'),{force:true});await mkdir(root,{recursive:true});
|
||||
const jobs=instances.flatMap(instance=>selected.map(op=>({instance,op})));const fixtures=await mapLimit(jobs,2,async({instance,op})=>{const f=await collectOne(instance,op);console.log(`${instance.alias} ${op.operationId} ${f.statusCategory}`);return {f,domain:op.upstreamDomain};});
|
||||
for(const {f,domain} of fixtures){const dir=path.join(root,safeName(domain));await mkdir(dir,{recursive:true});await writeFile(path.join(dir,`${f.sourceInstanceAlias}--${f.operationId}.json`),JSON.stringify(f,null,2)+'\n',{flag:'wx'});}
|
||||
const files=[];const coverage:Record<string,number>={};for(const full of await allJson(root)){const text=await readFile(full);const rel=path.relative(pkg,full);const domain=path.basename(path.dirname(full));coverage[domain]=(coverage[domain]||0)+1;files.push({path:rel,size:(await stat(full)).size,sha256:createHash('sha256').update(text).digest('hex')});}
|
||||
files.sort((a,b)=>a.path.localeCompare(b.path));const shapeBaseline={path:'src/response-shapes-58e2204.json',size:shapeBytes.length,sha256:createHash('sha256').update(shapeBytes).digest('hex')};await writeFile(path.join(pkg,'src/manifest.json'),JSON.stringify({schemaVersion:1,upstreamBaseline:'58e2204',realFixtureCount:files.length,syntheticFixtureCount:0,domainCoverage:Object.fromEntries(Object.entries(coverage).sort()),shapeBaseline,files},null,2)+'\n');
|
||||
}
|
||||
if(process.argv[1]===fileURLToPath(import.meta.url))main().catch(()=>{console.error('collector failed');process.exitCode=1;});
|
||||
@@ -0,0 +1,64 @@
|
||||
import { redactOperationPayload } from '../src/redactor.ts';
|
||||
import { upstream58e2204Operations } from '../../operation-registry/src/upstream-58e2204.ts';
|
||||
|
||||
export const UPSTREAM_BASELINE='58e2204';
|
||||
export const DENY_REASONS:Record<string,string>={
|
||||
'/api/network/operators/scan':'active radio/network scan',
|
||||
'/api/connectivity':'handler performs active connectivity ping',
|
||||
'/api/sms/list':'message list can expose body; intentionally not collected',
|
||||
'/api/sms/conversation':'requires correspondent query and exposes message bodies',
|
||||
'/api/call/history':'query limit is not encoded in Registry path contract',
|
||||
'/api/notifications/logs':'query limit is not encoded in Registry path contract',
|
||||
'/api/notifications/queue':'query limit is not encoded in Registry path contract',
|
||||
'/api/automation/logs':'query limit is not encoded in Registry path contract',
|
||||
'/api/device-network/ddns/logs':'no safe limit contract',
|
||||
'/api/esim/euicc':'query-bearing endpoint omitted',
|
||||
'/api/esim/profiles':'query-bearing endpoint omitted',
|
||||
};
|
||||
const registryById=new Map(upstream58e2204Operations.map((op:any)=>[op.operationId,op]));
|
||||
// Capture the complete imported JSON contract before any caller can mutate it.
|
||||
// Exact identity alone does not protect mutable Registry objects.
|
||||
const registrySignatures=new Map(upstream58e2204Operations.map((op:any)=>[op.operationId,JSON.stringify(op)]));
|
||||
function isPrivateLan(host:string){const p=host.split('.').map(Number);return p.length===4&&p.every(n=>Number.isInteger(n)&&n>=0&&n<=255)&&(p[0]===10||(p[0]===172&&p[1]>=16&&p[1]<=31)||(p[0]===192&&p[1]===168));}
|
||||
export function validateInstanceOrigin(origin:string){
|
||||
let u:URL;try{u=new URL(origin);}catch{throw new Error('invalid collector instance origin');}
|
||||
if(!['http:','https:'].includes(u.protocol)||!isPrivateLan(u.hostname)||u.pathname!=='/'||u.search||u.hash||u.username||u.password)throw new Error('invalid collector instance origin');
|
||||
return u.origin;
|
||||
}
|
||||
function validatePath(path:string){
|
||||
if(typeof path!=='string'||!path.startsWith('/api/')||path.startsWith('//')||path.includes('\\')||/[?#%]/.test(path)||path.includes('//')) throw new Error('unsafe operation path');
|
||||
let decoded=path; for(let i=0;i<2;i++){const next=decodeURIComponent(decoded);if(next!==decoded)throw new Error('encoded operation path');decoded=next;}
|
||||
if(path.split('/').some(x=>x==='.'||x==='..'))throw new Error('dot segment operation path');
|
||||
return path;
|
||||
}
|
||||
export function validateReadonlyOperation(op:any){
|
||||
const exact=registryById.get(op?.operationId);
|
||||
if(!exact||exact!==op||registrySignatures.get(op?.operationId)!==JSON.stringify(op))throw new Error('operation violates Registry integrity');
|
||||
validatePath(op.pathTemplate);
|
||||
if(op.method!=='GET'||op.riskLevel!=='R0'||/[{}]/.test(op.pathTemplate))throw new Error('operation is not readonly R0');
|
||||
if(DENY_REASONS[op.pathTemplate])throw new Error('operation is explicitly denied');
|
||||
return op;
|
||||
}
|
||||
export function selectReadonlyOperations(registry:any[]){
|
||||
const candidates=registry.filter(o=>o.method==='GET'&&o.riskLevel==='R0'&&!/[{}]/.test(o.pathTemplate));
|
||||
for(const op of candidates) validatePath(op.pathTemplate);
|
||||
return {selected:candidates.filter(o=>!DENY_REASONS[o.pathTemplate]).map(validateReadonlyOperation),denied:candidates.filter(o=>DENY_REASONS[o.pathTemplate]).map(o=>({...o,denyReason:DENY_REASONS[o.pathTemplate]}))};
|
||||
}
|
||||
function latency(ms:number,timedOut=false){if(timedOut)return 'timeout';if(ms<250)return '<250ms';if(ms<1000)return '250ms-1s';if(ms<3000)return '1-3s';return '>3s';}
|
||||
function statusCategory(status:number,isJson:boolean){if(status===401||status===403)return 'auth-required';if(status===404||status===405||status===501)return 'unsupported';if(status>=200&&status<300)return isJson?'success':'non-json';return 'http-error';}
|
||||
export async function collectOne(instance:{origin:string,alias:string},op:any,transport:any=fetch){
|
||||
validateReadonlyOperation(op);
|
||||
if(!['instance-1','instance-2'].includes(instance?.alias))throw new Error('invalid instance alias');
|
||||
const origin=validateInstanceOrigin(instance?.origin);
|
||||
const requestUrl=new URL(op.pathTemplate,origin);
|
||||
if(requestUrl.origin!==origin||requestUrl.pathname!==op.pathTemplate||requestUrl.search||requestUrl.hash)throw new Error('unsafe collector request URL');
|
||||
const started=Date.now();const controller=new AbortController();const timer=setTimeout(()=>controller.abort(),6000);
|
||||
const base={schemaVersion:1,upstreamBaseline:UPSTREAM_BASELINE,capturedAt:new Date().toISOString().slice(0,10),sourceInstanceAlias:instance.alias,operationId:op.operationId,method:'GET',pathTemplate:op.pathTemplate,redacted:true};
|
||||
try{
|
||||
validateReadonlyOperation(op);
|
||||
const response=await transport(requestUrl,{method:'GET',headers:{accept:'application/json'},body:undefined,credentials:'omit',redirect:'manual',signal:controller.signal});
|
||||
const contentType=(response.headers.get('content-type')||'').split(';')[0].trim().toLowerCase()||null;const claimsJson=contentType==='application/json'||contentType?.endsWith('+json');
|
||||
let parsedJson=false;let payload:any;if(claimsJson){try{payload=await response.json();parsedJson=true;}catch{payload={error:'invalid-json'};}}else{await response.text();payload={text:'[REDACTED]'};}
|
||||
return {...base,statusCategory:statusCategory(response.status,parsedJson),httpStatus:response.status,contentType,latencyBucket:latency(Date.now()-started),payload:redactOperationPayload(payload,op)};
|
||||
}catch(error:any){if(String(error?.message||'').includes('Registry')||String(error?.message||'').includes('operation'))throw error;const timeout=error?.name==='AbortError';return {...base,statusCategory:timeout?'timeout':'network-error',httpStatus:null,contentType:null,latencyBucket:latency(Date.now()-started,timeout),payload:{error:timeout?'timeout':'network-error'}};}finally{clearTimeout(timer);}
|
||||
}
|
||||
@@ -0,0 +1,418 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"upstreamBaseline": "58e2204",
|
||||
"realFixtureCount": 78,
|
||||
"syntheticFixtureCount": 0,
|
||||
"domainCoverage": {
|
||||
"automation": 2,
|
||||
"calls": 12,
|
||||
"cellular": 10,
|
||||
"data-connection": 10,
|
||||
"device-network": 12,
|
||||
"device-system": 6,
|
||||
"instances-auth": 6,
|
||||
"messages": 2,
|
||||
"notifications": 2,
|
||||
"ota": 2,
|
||||
"radio-lock": 6,
|
||||
"sim": 2,
|
||||
"workmode-esim": 6
|
||||
},
|
||||
"files": [
|
||||
{
|
||||
"path": "src/simadmin/automation/instance-1--getAutomationConfig.json",
|
||||
"size": 411,
|
||||
"sha256": "d11fc843d9c120792d5c3c46cc2bfc23a46199fcbbea4d69a5f01f41c246d345"
|
||||
},
|
||||
{
|
||||
"path": "src/simadmin/automation/instance-2--getAutomationConfig.json",
|
||||
"size": 411,
|
||||
"sha256": "7644caa3c9b42e417412fa79c1ffd40ff8d32b0ec5f5c180a74169e2a8fa6f4e"
|
||||
},
|
||||
{
|
||||
"path": "src/simadmin/calls/instance-1--getCallForwarding.json",
|
||||
"size": 487,
|
||||
"sha256": "c4d8cff204ff3abfbca65eae620e575a4f1214df91ee8c48f8c40b98ac4e5b4f"
|
||||
},
|
||||
{
|
||||
"path": "src/simadmin/calls/instance-1--getCalls.json",
|
||||
"size": 446,
|
||||
"sha256": "ad2132947c5ca59820e808fbaedb7ee8f808def626af4241d4665b44e05220bb"
|
||||
},
|
||||
{
|
||||
"path": "src/simadmin/calls/instance-1--getCallSettings.json",
|
||||
"size": 796,
|
||||
"sha256": "64268df9112a40400c40fd38cb629046b66ed007f251895adc6e1464f38df286"
|
||||
},
|
||||
{
|
||||
"path": "src/simadmin/calls/instance-1--getCallVolume.json",
|
||||
"size": 483,
|
||||
"sha256": "816e75f1356da18712a12c1d9e4e65b949349590c13e79b16146a2e55e3de112"
|
||||
},
|
||||
{
|
||||
"path": "src/simadmin/calls/instance-1--getImsStatus.json",
|
||||
"size": 472,
|
||||
"sha256": "7c38e2a3afe05aa68df1bc5d1747d556e7d623e183dddfd5f79093e566602b33"
|
||||
},
|
||||
{
|
||||
"path": "src/simadmin/calls/instance-1--getVoicemailStatus.json",
|
||||
"size": 490,
|
||||
"sha256": "61066e2ae5122adaeed410a3f628872cbb475df9b6d1fa9fb74165f6a102216b"
|
||||
},
|
||||
{
|
||||
"path": "src/simadmin/calls/instance-2--getCallForwarding.json",
|
||||
"size": 487,
|
||||
"sha256": "8a20cd0ad05764ed520c2d484ac092a30b03beccdcc00b9e23d795a8e35c5087"
|
||||
},
|
||||
{
|
||||
"path": "src/simadmin/calls/instance-2--getCalls.json",
|
||||
"size": 446,
|
||||
"sha256": "3947b6a4f96e76515ec2fe0d2778eaea12dca468a1ccf6f642c252a2d3befe15"
|
||||
},
|
||||
{
|
||||
"path": "src/simadmin/calls/instance-2--getCallSettings.json",
|
||||
"size": 796,
|
||||
"sha256": "ced02cdde68e46fdce377312a3f13b146a71e986a983ad826f23d3e654937082"
|
||||
},
|
||||
{
|
||||
"path": "src/simadmin/calls/instance-2--getCallVolume.json",
|
||||
"size": 483,
|
||||
"sha256": "46b753951be5cf88602cd757078665a4569a9f6af29f5652d8a2589918cbd1bc"
|
||||
},
|
||||
{
|
||||
"path": "src/simadmin/calls/instance-2--getImsStatus.json",
|
||||
"size": 472,
|
||||
"sha256": "cc0fe42dc6367061cb6655fd7eff2a080b9f4c7656a4f6085a2675b7b015c6c6"
|
||||
},
|
||||
{
|
||||
"path": "src/simadmin/calls/instance-2--getVoicemailStatus.json",
|
||||
"size": 490,
|
||||
"sha256": "09444cd4b71f10414a481e31c8f05199b4cff1b5388ac3223279b3e7507859cb"
|
||||
},
|
||||
{
|
||||
"path": "src/simadmin/cellular/instance-1--getCells.json",
|
||||
"size": 859,
|
||||
"sha256": "939806fafaa6e377ea163d8d1c4b2a471a354e27ede421e9086b01493fd99354"
|
||||
},
|
||||
{
|
||||
"path": "src/simadmin/cellular/instance-1--getLocationCellInfo.json",
|
||||
"size": 1005,
|
||||
"sha256": "b1802511d2fb18040923185cf71c5419a9553fa60107e1fad04146f3cb9fe450"
|
||||
},
|
||||
{
|
||||
"path": "src/simadmin/cellular/instance-1--getNetwork.json",
|
||||
"size": 631,
|
||||
"sha256": "8fb2a9b1c17402d2f21912ff66b66291de12f8fd2c9b9e43058370c7f94f2671"
|
||||
},
|
||||
{
|
||||
"path": "src/simadmin/cellular/instance-1--getNetworkOperators.json",
|
||||
"size": 714,
|
||||
"sha256": "73da8d7763fd82530a51c6fce5c0ff924a1778d71861c4fd9f29d061609d1c7b"
|
||||
},
|
||||
{
|
||||
"path": "src/simadmin/cellular/instance-1--getNetworkSignalStrength.json",
|
||||
"size": 486,
|
||||
"sha256": "2999ddf03251b6b896c54e7d70eb37f5d9c96ea89f271174a37ddd50eb0765a3"
|
||||
},
|
||||
{
|
||||
"path": "src/simadmin/cellular/instance-2--getCells.json",
|
||||
"size": 859,
|
||||
"sha256": "5c6bbf23e612db363e728f8ed344a1b8fcb7d3a35adab14e6cd2eaac1debf92b"
|
||||
},
|
||||
{
|
||||
"path": "src/simadmin/cellular/instance-2--getLocationCellInfo.json",
|
||||
"size": 1005,
|
||||
"sha256": "dc1b0a6f59951c23fae19ef09b60d9dcfd7b369f0aab2b9c37a036ea9a3e45ad"
|
||||
},
|
||||
{
|
||||
"path": "src/simadmin/cellular/instance-2--getNetwork.json",
|
||||
"size": 631,
|
||||
"sha256": "ff187f0305c84ff13c68cdaa8b5d2f0edc85ba08403d61a484c76244c106d583"
|
||||
},
|
||||
{
|
||||
"path": "src/simadmin/cellular/instance-2--getNetworkOperators.json",
|
||||
"size": 714,
|
||||
"sha256": "6d6259f5560f2abe67f6afaaa98c84aeb6fcf6b0cccd093f4ef261ebd91d5043"
|
||||
},
|
||||
{
|
||||
"path": "src/simadmin/cellular/instance-2--getNetworkSignalStrength.json",
|
||||
"size": 486,
|
||||
"sha256": "d7362c1857f457831b8284375a288ff38109d6458fbd27f204e5fc77698ccbdc"
|
||||
},
|
||||
{
|
||||
"path": "src/simadmin/data-connection/instance-1--getAirplaneMode.json",
|
||||
"size": 514,
|
||||
"sha256": "5aa56c71ca7b7fb61ae32e97aaa8fa2a5043a0ae2a6847667a37dba4f1353e75"
|
||||
},
|
||||
{
|
||||
"path": "src/simadmin/data-connection/instance-1--getApn.json",
|
||||
"size": 761,
|
||||
"sha256": "daa957d911d712c44cfd0daa6def5cd094bf2ed9a25e8345a063169cd29b6b85"
|
||||
},
|
||||
{
|
||||
"path": "src/simadmin/data-connection/instance-1--getBasebandRestartStatus.json",
|
||||
"size": 507,
|
||||
"sha256": "9eb8f1fe2d183b2ce0ce1a4e8db5fbd0173b874c2a921cf483ca41f0801540bf"
|
||||
},
|
||||
{
|
||||
"path": "src/simadmin/data-connection/instance-1--getData.json",
|
||||
"size": 451,
|
||||
"sha256": "250fe59ff07a353596fcfc286b90ac9be6cd59dfe1f115a17bb52fba5eef96f5"
|
||||
},
|
||||
{
|
||||
"path": "src/simadmin/data-connection/instance-1--getRoaming.json",
|
||||
"size": 493,
|
||||
"sha256": "3793690eea13d9ec58e01aa2efdce29132e6cc47c470f8e9ef0c3906c33e2ec6"
|
||||
},
|
||||
{
|
||||
"path": "src/simadmin/data-connection/instance-2--getAirplaneMode.json",
|
||||
"size": 514,
|
||||
"sha256": "6edc530b74bb390ef329cf75739bbb7a4043d8007c44af34b2a89c2f4f5b985c"
|
||||
},
|
||||
{
|
||||
"path": "src/simadmin/data-connection/instance-2--getApn.json",
|
||||
"size": 761,
|
||||
"sha256": "0a4e0e6e21707910646613722ad1a52348a3cfaf178017d9100293fb116b5b8f"
|
||||
},
|
||||
{
|
||||
"path": "src/simadmin/data-connection/instance-2--getBasebandRestartStatus.json",
|
||||
"size": 507,
|
||||
"sha256": "7656ea60e5f4fa138e968da910e2f536f84fcf40001d8cdcbf8472d228297979"
|
||||
},
|
||||
{
|
||||
"path": "src/simadmin/data-connection/instance-2--getData.json",
|
||||
"size": 451,
|
||||
"sha256": "c79d7a33478229c779ea221f2d8d824f3302e9c6d577eadef2640676f8bfb12b"
|
||||
},
|
||||
{
|
||||
"path": "src/simadmin/data-connection/instance-2--getRoaming.json",
|
||||
"size": 493,
|
||||
"sha256": "25d80ea1f2b4265a9306267c8b9182ce5c73dc9c82fd0c5faaf4e955fa1d2111"
|
||||
},
|
||||
{
|
||||
"path": "src/simadmin/device-network/instance-1--getDeviceNetworkDdnsConfig.json",
|
||||
"size": 1247,
|
||||
"sha256": "a196f88ca3683bcb88cb40043e2073f65f8a467de384a903031439cb9af8da5b"
|
||||
},
|
||||
{
|
||||
"path": "src/simadmin/device-network/instance-1--getDeviceNetworkDdnsStatus.json",
|
||||
"size": 657,
|
||||
"sha256": "85f4a794f3bf0dac8c1fbbc69e2054f9b1c9a8ba2d36e4d76e0dd1dd8f080849"
|
||||
},
|
||||
{
|
||||
"path": "src/simadmin/device-network/instance-1--getDeviceNetworkWlanProfiles.json",
|
||||
"size": 705,
|
||||
"sha256": "fe06ef527f06715c6aace36bb1a4f15e553f46ffe30eaa9e91d374c3ac50d7b3"
|
||||
},
|
||||
{
|
||||
"path": "src/simadmin/device-network/instance-1--getDeviceNetworkWlanStatus.json",
|
||||
"size": 862,
|
||||
"sha256": "c10e9bda965951db993a647122d9eef311cf65d92f12432942a0050439e0bc3e"
|
||||
},
|
||||
{
|
||||
"path": "src/simadmin/device-network/instance-1--getNetworkConnectionAddresses.json",
|
||||
"size": 654,
|
||||
"sha256": "ef8adddeb80fba7380ccd7f7aa8cfe3d923dd3cd6b784105bd7379e0de3b72ba"
|
||||
},
|
||||
{
|
||||
"path": "src/simadmin/device-network/instance-1--getNetworkInterfaces.json",
|
||||
"size": 7178,
|
||||
"sha256": "e002a51e824fc940eae82d9853bc4dda70c43ccfadaf46165f18511406c70f34"
|
||||
},
|
||||
{
|
||||
"path": "src/simadmin/device-network/instance-2--getDeviceNetworkDdnsConfig.json",
|
||||
"size": 1247,
|
||||
"sha256": "52a0844767bfe4eaa1bbfa416f12a33c19e32d32a6f3ee81603399655c48fe87"
|
||||
},
|
||||
{
|
||||
"path": "src/simadmin/device-network/instance-2--getDeviceNetworkDdnsStatus.json",
|
||||
"size": 657,
|
||||
"sha256": "95818e5300b7b2d4a863c63c470e136c6c1c632825e0affec9fa664bd905decb"
|
||||
},
|
||||
{
|
||||
"path": "src/simadmin/device-network/instance-2--getDeviceNetworkWlanProfiles.json",
|
||||
"size": 703,
|
||||
"sha256": "1f72f32f15c333d3252751f0c223849071e68dd879b7d76a543c2e14d18e91cd"
|
||||
},
|
||||
{
|
||||
"path": "src/simadmin/device-network/instance-2--getDeviceNetworkWlanStatus.json",
|
||||
"size": 862,
|
||||
"sha256": "b0ba613f078e56cc6bf4d9e7736abaf6a941b6f1c4646527e1194da3775d3f7d"
|
||||
},
|
||||
{
|
||||
"path": "src/simadmin/device-network/instance-2--getNetworkConnectionAddresses.json",
|
||||
"size": 654,
|
||||
"sha256": "0b69ebe7267a762ef68ce96f692fe85097cc918588cb138cb5ad81e03885cbae"
|
||||
},
|
||||
{
|
||||
"path": "src/simadmin/device-network/instance-2--getNetworkInterfaces.json",
|
||||
"size": 7178,
|
||||
"sha256": "9d5630a639da04e3b97bdd3c8bb45324aa30689e63e483507cb4af19750eed0f"
|
||||
},
|
||||
{
|
||||
"path": "src/simadmin/device-system/instance-1--getDevice.json",
|
||||
"size": 606,
|
||||
"sha256": "521824ff36c00840077ef62b4b4b682804ee631df92f8d5849f8e6b7c250508f"
|
||||
},
|
||||
{
|
||||
"path": "src/simadmin/device-system/instance-1--getStats.json",
|
||||
"size": 2571,
|
||||
"sha256": "3677b4c77941ddf7da7c1c6f73bf923adcbbda413d0327516a39c66cbb9f959f"
|
||||
},
|
||||
{
|
||||
"path": "src/simadmin/device-system/instance-1--getStatsCpu.json",
|
||||
"size": 2070,
|
||||
"sha256": "a7b1303f8d70dfb7c6ce078c1f1cd8a55cc554d05400f8948adccc6fc2bd4cd2"
|
||||
},
|
||||
{
|
||||
"path": "src/simadmin/device-system/instance-2--getDevice.json",
|
||||
"size": 604,
|
||||
"sha256": "a19499a4cd0f5c5022e167d9c5871c367f37f6292e17bc03dc3d40c9ccef6663"
|
||||
},
|
||||
{
|
||||
"path": "src/simadmin/device-system/instance-2--getStats.json",
|
||||
"size": 2568,
|
||||
"sha256": "66394c29165b906613d4f083e557deecaad26b080ff94080b990555991ae1018"
|
||||
},
|
||||
{
|
||||
"path": "src/simadmin/device-system/instance-2--getStatsCpu.json",
|
||||
"size": 2070,
|
||||
"sha256": "427fad157dabc8ac1227db4f9114a83d45da11aefc9d8f4a7065de749959dc0c"
|
||||
},
|
||||
{
|
||||
"path": "src/simadmin/instances-auth/instance-1--getAuthSettings.json",
|
||||
"size": 403,
|
||||
"sha256": "fce6c71a20f6e99744d40ebda8c570f5dd3dffe475ce25ce08fb3eff859339fc"
|
||||
},
|
||||
{
|
||||
"path": "src/simadmin/instances-auth/instance-1--getAuthStatus.json",
|
||||
"size": 399,
|
||||
"sha256": "23eba110da227cba6112f2ad848c980f94f69597e0697a08290d134771f40c91"
|
||||
},
|
||||
{
|
||||
"path": "src/simadmin/instances-auth/instance-1--getHealth.json",
|
||||
"size": 478,
|
||||
"sha256": "000ce1f086ba62a206ca0fdff3a157524e49c6d2879ef21e6add08826cf7f2ad"
|
||||
},
|
||||
{
|
||||
"path": "src/simadmin/instances-auth/instance-2--getAuthSettings.json",
|
||||
"size": 784,
|
||||
"sha256": "f22ad6b7b13ccdd279c9e26722a0f64395a12c184fc26429ca365ab761789f9b"
|
||||
},
|
||||
{
|
||||
"path": "src/simadmin/instances-auth/instance-2--getAuthStatus.json",
|
||||
"size": 809,
|
||||
"sha256": "2860f468c3da7d390bd30bdd184517da6d1272b06ecbdc472031fb6206a22f1c"
|
||||
},
|
||||
{
|
||||
"path": "src/simadmin/instances-auth/instance-2--getHealth.json",
|
||||
"size": 478,
|
||||
"sha256": "9d7120b284db3008d00439f5e117c287336a269be4835e50db89a7f0806ec503"
|
||||
},
|
||||
{
|
||||
"path": "src/simadmin/messages/instance-1--getSmsStats.json",
|
||||
"size": 499,
|
||||
"sha256": "086e7bb4d33a4ce66e86eb781ed5be1cb63ba01f95170befc0ddec3db690ba7c"
|
||||
},
|
||||
{
|
||||
"path": "src/simadmin/messages/instance-2--getSmsStats.json",
|
||||
"size": 545,
|
||||
"sha256": "702bb29db443164ac95a2fb66433749b9b8d8bae4cb63de17187043a26b4cf09"
|
||||
},
|
||||
{
|
||||
"path": "src/simadmin/notifications/instance-1--getNotificationsConfig.json",
|
||||
"size": 4857,
|
||||
"sha256": "872b49d3a8c38d7f87012dad212b3fab2c6e4a8c5949c2284140e36d8a0e81b0"
|
||||
},
|
||||
{
|
||||
"path": "src/simadmin/notifications/instance-2--getNotificationsConfig.json",
|
||||
"size": 2377,
|
||||
"sha256": "8586332d14c7e2f9c807c5e62f3c865f0b412af17357f86aff62919bf9021052"
|
||||
},
|
||||
{
|
||||
"path": "src/simadmin/ota/instance-1--getOtaStatus.json",
|
||||
"size": 539,
|
||||
"sha256": "72b7d0792f49d1f4aabf0c59c80cae9259f8ad280f0f5db15875906476cbf294"
|
||||
},
|
||||
{
|
||||
"path": "src/simadmin/ota/instance-2--getOtaStatus.json",
|
||||
"size": 539,
|
||||
"sha256": "e5d0449dedbdea5f0bc304b4a45a10da0d773955c4408a958ffb10872c9044c4"
|
||||
},
|
||||
{
|
||||
"path": "src/simadmin/radio-lock/instance-1--getBandLock.json",
|
||||
"size": 864,
|
||||
"sha256": "c24e823e8c3f5ff40451fb6e1ac3a46189072ca17b05d7e16d93008ec80cd06d"
|
||||
},
|
||||
{
|
||||
"path": "src/simadmin/radio-lock/instance-1--getCellLock.json",
|
||||
"size": 837,
|
||||
"sha256": "e53efdbd885ac11899ac5fe7b66d7174fb6ad3c8eaefaee3c2998d47dff41567"
|
||||
},
|
||||
{
|
||||
"path": "src/simadmin/radio-lock/instance-1--getRadioMode.json",
|
||||
"size": 573,
|
||||
"sha256": "35a728a1b34dccdbf8e6a8b2eb8ea43d3986ceb239406e2f31783f2095233368"
|
||||
},
|
||||
{
|
||||
"path": "src/simadmin/radio-lock/instance-2--getBandLock.json",
|
||||
"size": 864,
|
||||
"sha256": "5b0172f1b6d39d046a9a1be766e988aed921c990cb15ba6863ce3f302f3a21c0"
|
||||
},
|
||||
{
|
||||
"path": "src/simadmin/radio-lock/instance-2--getCellLock.json",
|
||||
"size": 837,
|
||||
"sha256": "4bb27d837265d3f9e1b2bd8ccf247af6bc3dab17d7bb65e10af7bf18359073da"
|
||||
},
|
||||
{
|
||||
"path": "src/simadmin/radio-lock/instance-2--getRadioMode.json",
|
||||
"size": 573,
|
||||
"sha256": "7ad89ba63089b98763da4bc6e544188356661dc7c1710e73eb047af906e2685c"
|
||||
},
|
||||
{
|
||||
"path": "src/simadmin/sim/instance-1--getSim.json",
|
||||
"size": 712,
|
||||
"sha256": "3d98909a13f3801e63ac0d801998dc91462accfbf3e11c78a3322723475c9743"
|
||||
},
|
||||
{
|
||||
"path": "src/simadmin/sim/instance-2--getSim.json",
|
||||
"size": 709,
|
||||
"sha256": "9edba55b3592554831913ab544713c4e768267ae3f054d48e4d0bf0b61ffa746"
|
||||
},
|
||||
{
|
||||
"path": "src/simadmin/workmode-esim/instance-1--getEsimConfig.json",
|
||||
"size": 399,
|
||||
"sha256": "deb99d35aa9fca3149f5554d39bcaa6cd76046b3d48b9d8f6d8faa5050ac5ee8"
|
||||
},
|
||||
{
|
||||
"path": "src/simadmin/workmode-esim/instance-1--getEsimLpacStatus.json",
|
||||
"size": 476,
|
||||
"sha256": "045e57c7b75fc3e1edc03ffa025997dc03e9d95dff0967769389c8682df088dd"
|
||||
},
|
||||
{
|
||||
"path": "src/simadmin/workmode-esim/instance-1--getWorkMode.json",
|
||||
"size": 489,
|
||||
"sha256": "b8c0f72a5e1f260d74e1f487f14ed6b948f0f166e33d7379eb7ec6e4b76bac72"
|
||||
},
|
||||
{
|
||||
"path": "src/simadmin/workmode-esim/instance-2--getEsimConfig.json",
|
||||
"size": 399,
|
||||
"sha256": "09c46f91077fbb55491139d3d8c1a7e779835a056a8ed6cd872dbdcb8799f678"
|
||||
},
|
||||
{
|
||||
"path": "src/simadmin/workmode-esim/instance-2--getEsimLpacStatus.json",
|
||||
"size": 476,
|
||||
"sha256": "3b2fde972698b63efbdd7ef63199443fcb7d2ef682f41c6efa2c8078aaf6b336"
|
||||
},
|
||||
{
|
||||
"path": "src/simadmin/workmode-esim/instance-2--getWorkMode.json",
|
||||
"size": 489,
|
||||
"sha256": "2faeb2d4b715944403bde8b2732eb531779056ffc22fb6a1ee6ff3d88124ae10"
|
||||
}
|
||||
],
|
||||
"shapeBaseline": {
|
||||
"path": "src/response-shapes-58e2204.json",
|
||||
"size": 155410,
|
||||
"sha256": "812d5aebae34e5c8550a47ea6bada6a59645c224ee1dec3a82c8d5e28fa14163"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
const SECRET_KEY = /(?:password|passwd|credential|api[_-]?key|access[_-]?(?:key|token|secret|id)|device[_-]?key|private[_-]?key|pin|puk|username|(?:^|[_-])user(?:$|[_-])|account|serial|revision|(?:^|[_-])path(?:$|[_-])|template|token|secret|cookie|authorization|content|body|confirmation[_-]?code)/i;
|
||||
const STRUCTURED_KEY = /^(?:config)$/i;
|
||||
const LOCATION_KEY = /^(?:lat(?:itude)?|lon(?:gitude)?|cell_?id|cid|lac|tac|pci|e?nb|gnb)$/i;
|
||||
const FINGERPRINT_KEY = /^(?:uptime|traffic|temperature|.*(?:rx|tx)[_-]?(?:bytes|packets)|.*percent|(?:created|updated|captured)?_?at|timestamp|time)$/i;
|
||||
const SAFE_BOOLEAN_KEY = /(?:phone_number|sms_center)_is_manual$/i;
|
||||
const PHONE_KEY = /(?:phone|msisdn|recipient|number|smsc|sms_center)/i;
|
||||
const IDENTIFIER_KEY = /(?:iccid|imsi|imei|eid|matching_id)/i;
|
||||
const ADDRESS_KEY = /(?:ssid|bssid|mac|(?:^|_)(?:ip|address)(?:_|$)|hostname|host|url|webhook)/i;
|
||||
|
||||
export function typedRedact(value:any):any {
|
||||
if(Array.isArray(value)) return value.map(typedRedact);
|
||||
if(value && typeof value==='object') return Object.fromEntries(Object.entries(value).map(([k,v])=>[k,typedRedact(v)]));
|
||||
if(typeof value==='string') return '[REDACTED]';
|
||||
if(typeof value==='number') return 0;
|
||||
if(typeof value==='boolean') return false;
|
||||
return value;
|
||||
}
|
||||
|
||||
function looksHighEntropy(s:string){
|
||||
if(s.length<16 || /\s/.test(s)) return false;
|
||||
const classes=[/[a-z]/,/[A-Z]/,/\d/,/[^A-Za-z0-9]/].filter(r=>r.test(s)).length;
|
||||
return classes>=3 || (classes>=2 && new Set(s).size>=12);
|
||||
}
|
||||
function unsafeString(s:string){
|
||||
return /https?:\/\//i.test(s) || /(?:^|\s|["'=])(?:[a-z0-9-]+\.)+[a-z]{2,}(?::\d+)?(?:\/\S*)?/i.test(s) ||
|
||||
/\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/i.test(s) || /(?:[0-9a-f]{2}:){5}[0-9a-f]{2}/i.test(s) ||
|
||||
/(?:^|[^\w])(?:[0-9a-f]{0,4}:){2,}[0-9a-f:%]+(?:$|[^\w])/i.test(s) || /\b(?:\d{1,3}\.){3}\d{1,3}\b/.test(s) ||
|
||||
/(?:^|\D)\+?\d(?:[ ()-]*\d){7,}(?:$|\D)/.test(s) || /(?:\d[ -]){6,}\d/.test(s) ||
|
||||
/(?:^|\s)(?:\/Users\/|\/home\/|\/var\/|\/etc\/|[A-Za-z]:\\)\S+/i.test(s) ||
|
||||
/\b(?:token|user(?:name)?|account|path|url|host)\s*[=:]\s*\S+/i.test(s) || /\b(?:AKIA|ASIA)[A-Z0-9]{12,}\b/.test(s) || looksHighEntropy(s);
|
||||
}
|
||||
|
||||
class Context {
|
||||
value(value:any,key='',force=false):any {
|
||||
if(SAFE_BOOLEAN_KEY.test(key) && typeof value==='boolean') return value;
|
||||
if(SECRET_KEY.test(key) || LOCATION_KEY.test(key) || FINGERPRINT_KEY.test(key)) return typedRedact(value);
|
||||
if(STRUCTURED_KEY.test(key)) force=true;
|
||||
if(PHONE_KEY.test(key)||IDENTIFIER_KEY.test(key)||ADDRESS_KEY.test(key)) return typedRedact(value);
|
||||
if(Array.isArray(value)) return value.map(v=>this.value(v,'',force));
|
||||
if(value && typeof value==='object') return Object.fromEntries(Object.entries(value).map(([k,v])=>[k,this.value(v,k,force)]));
|
||||
if(force) return typedRedact(value);
|
||||
if(typeof value==='string'){
|
||||
const t=value.trim();
|
||||
if((t.startsWith('{')&&t.endsWith('}'))||(t.startsWith('[')&&t.endsWith(']'))){try{return JSON.stringify(this.value(JSON.parse(value)));}catch{}}
|
||||
return unsafeString(value)?'[REDACTED]':value;
|
||||
}
|
||||
if(typeof value==='number' && Number.isInteger(value) && Math.abs(value)>=100_000_000) return 0;
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
export function parseResponseSensitivePath(path:string):string[] {
|
||||
if(typeof path!=='string'||!path.startsWith('$.response')) throw new Error('unsupported response sensitive path');
|
||||
const rest=path.slice('$.response'.length);
|
||||
if(!rest) return [];
|
||||
if(!/^(?:\.[A-Za-z0-9_-]+|\[\*\])+$/.test(rest)) throw new Error('unsupported response sensitive path');
|
||||
return [...rest.matchAll(/\.([A-Za-z0-9_-]+)|\[\*\]/g)].map(m=>m[1]||'*');
|
||||
}
|
||||
export function applySensitivePath(node:any,parts:string[],index=0):number {
|
||||
if(index===parts.length) return 0;
|
||||
const part=parts[index];
|
||||
if(part==='*'){
|
||||
if(!Array.isArray(node)) return 0;
|
||||
if(index===parts.length-1){for(let i=0;i<node.length;i++)node[i]=typedRedact(node[i]);return node.length;}
|
||||
return node.reduce((n,item)=>n+applySensitivePath(item,parts,index+1),0);
|
||||
}
|
||||
if(!node||typeof node!=='object'||!(part in node)) return 0;
|
||||
if(index===parts.length-1){node[part]=typedRedact(node[part]);return 1;}
|
||||
return applySensitivePath(node[part],parts,index+1);
|
||||
}
|
||||
export function redactOperationPayload<T>(value:T,operation:any):T {
|
||||
const clone=structuredClone(value);
|
||||
for(const field of operation?.sensitiveFields||[]){
|
||||
if(field?.direction!=='response') continue;
|
||||
const parts=parseResponseSensitivePath(field.path);
|
||||
// The decoded HTTP body corresponds to $.response; parts are body-relative
|
||||
// (normally beginning with `data`), so do not add another response wrapper.
|
||||
applySensitivePath(clone,parts);
|
||||
}
|
||||
return new Context().value(clone) as T;
|
||||
}
|
||||
export function redact<T>(value:T):T { return new Context().value(value) as T; }
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"upstreamBaseline": "58e2204",
|
||||
"capturedAt": "2026-07-15",
|
||||
"sourceInstanceAlias": "instance-1",
|
||||
"operationId": "getAutomationConfig",
|
||||
"method": "GET",
|
||||
"pathTemplate": "/api/automation/config",
|
||||
"redacted": true,
|
||||
"statusCategory": "unsupported",
|
||||
"httpStatus": 404,
|
||||
"contentType": "text/plain",
|
||||
"latencyBucket": "<250ms",
|
||||
"payload": {
|
||||
"text": "[REDACTED]"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"upstreamBaseline": "58e2204",
|
||||
"capturedAt": "2026-07-15",
|
||||
"sourceInstanceAlias": "instance-2",
|
||||
"operationId": "getAutomationConfig",
|
||||
"method": "GET",
|
||||
"pathTemplate": "/api/automation/config",
|
||||
"redacted": true,
|
||||
"statusCategory": "unsupported",
|
||||
"httpStatus": 404,
|
||||
"contentType": "text/plain",
|
||||
"latencyBucket": "<250ms",
|
||||
"payload": {
|
||||
"text": "[REDACTED]"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"upstreamBaseline": "58e2204",
|
||||
"capturedAt": "2026-07-15",
|
||||
"sourceInstanceAlias": "instance-1",
|
||||
"operationId": "getCallForwarding",
|
||||
"method": "GET",
|
||||
"pathTemplate": "/api/call/forwarding",
|
||||
"redacted": true,
|
||||
"statusCategory": "success",
|
||||
"httpStatus": 200,
|
||||
"contentType": "application/json",
|
||||
"latencyBucket": "<250ms",
|
||||
"payload": {
|
||||
"status": "error",
|
||||
"message": "Call forwarding is not exposed by ModemManager on this backend"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"upstreamBaseline": "58e2204",
|
||||
"capturedAt": "2026-07-15",
|
||||
"sourceInstanceAlias": "instance-1",
|
||||
"operationId": "getCallSettings",
|
||||
"method": "GET",
|
||||
"pathTemplate": "/api/call/settings",
|
||||
"redacted": true,
|
||||
"statusCategory": "success",
|
||||
"httpStatus": 200,
|
||||
"contentType": "application/json",
|
||||
"latencyBucket": "<250ms",
|
||||
"payload": {
|
||||
"status": "ok",
|
||||
"message": "Success",
|
||||
"data": {
|
||||
"calling_line_presentation": "unknown",
|
||||
"calling_name_presentation": "unknown",
|
||||
"connected_line_presentation": "unknown",
|
||||
"connected_line_restriction": "unknown",
|
||||
"called_line_presentation": "unknown",
|
||||
"calling_line_restriction": "unknown",
|
||||
"hide_caller_id": "unknown",
|
||||
"voice_call_waiting": "unknown"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"upstreamBaseline": "58e2204",
|
||||
"capturedAt": "2026-07-15",
|
||||
"sourceInstanceAlias": "instance-1",
|
||||
"operationId": "getCallVolume",
|
||||
"method": "GET",
|
||||
"pathTemplate": "/api/call/volume",
|
||||
"redacted": true,
|
||||
"statusCategory": "success",
|
||||
"httpStatus": 200,
|
||||
"contentType": "application/json",
|
||||
"latencyBucket": "<250ms",
|
||||
"payload": {
|
||||
"status": "error",
|
||||
"message": "Call volume control is not exposed by ModemManager on this backend"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"upstreamBaseline": "58e2204",
|
||||
"capturedAt": "2026-07-15",
|
||||
"sourceInstanceAlias": "instance-1",
|
||||
"operationId": "getCalls",
|
||||
"method": "GET",
|
||||
"pathTemplate": "/api/calls",
|
||||
"redacted": true,
|
||||
"statusCategory": "success",
|
||||
"httpStatus": 200,
|
||||
"contentType": "application/json",
|
||||
"latencyBucket": ">3s",
|
||||
"payload": {
|
||||
"status": "ok",
|
||||
"message": "Success",
|
||||
"data": {
|
||||
"calls": []
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"upstreamBaseline": "58e2204",
|
||||
"capturedAt": "2026-07-15",
|
||||
"sourceInstanceAlias": "instance-1",
|
||||
"operationId": "getImsStatus",
|
||||
"method": "GET",
|
||||
"pathTemplate": "/api/ims/status",
|
||||
"redacted": true,
|
||||
"statusCategory": "success",
|
||||
"httpStatus": 200,
|
||||
"contentType": "application/json",
|
||||
"latencyBucket": "<250ms",
|
||||
"payload": {
|
||||
"status": "error",
|
||||
"message": "IMS status is not exposed by ModemManager on this backend"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"upstreamBaseline": "58e2204",
|
||||
"capturedAt": "2026-07-15",
|
||||
"sourceInstanceAlias": "instance-1",
|
||||
"operationId": "getVoicemailStatus",
|
||||
"method": "GET",
|
||||
"pathTemplate": "/api/voicemail/status",
|
||||
"redacted": true,
|
||||
"statusCategory": "success",
|
||||
"httpStatus": 200,
|
||||
"contentType": "application/json",
|
||||
"latencyBucket": "<250ms",
|
||||
"payload": {
|
||||
"status": "error",
|
||||
"message": "Voicemail status is not exposed by ModemManager on this backend"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"upstreamBaseline": "58e2204",
|
||||
"capturedAt": "2026-07-15",
|
||||
"sourceInstanceAlias": "instance-2",
|
||||
"operationId": "getCallForwarding",
|
||||
"method": "GET",
|
||||
"pathTemplate": "/api/call/forwarding",
|
||||
"redacted": true,
|
||||
"statusCategory": "success",
|
||||
"httpStatus": 200,
|
||||
"contentType": "application/json",
|
||||
"latencyBucket": "<250ms",
|
||||
"payload": {
|
||||
"status": "error",
|
||||
"message": "Call forwarding is not exposed by ModemManager on this backend"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"upstreamBaseline": "58e2204",
|
||||
"capturedAt": "2026-07-15",
|
||||
"sourceInstanceAlias": "instance-2",
|
||||
"operationId": "getCallSettings",
|
||||
"method": "GET",
|
||||
"pathTemplate": "/api/call/settings",
|
||||
"redacted": true,
|
||||
"statusCategory": "success",
|
||||
"httpStatus": 200,
|
||||
"contentType": "application/json",
|
||||
"latencyBucket": "<250ms",
|
||||
"payload": {
|
||||
"status": "ok",
|
||||
"message": "Success",
|
||||
"data": {
|
||||
"calling_line_presentation": "unknown",
|
||||
"calling_name_presentation": "unknown",
|
||||
"connected_line_presentation": "unknown",
|
||||
"connected_line_restriction": "unknown",
|
||||
"called_line_presentation": "unknown",
|
||||
"calling_line_restriction": "unknown",
|
||||
"hide_caller_id": "unknown",
|
||||
"voice_call_waiting": "unknown"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"upstreamBaseline": "58e2204",
|
||||
"capturedAt": "2026-07-15",
|
||||
"sourceInstanceAlias": "instance-2",
|
||||
"operationId": "getCallVolume",
|
||||
"method": "GET",
|
||||
"pathTemplate": "/api/call/volume",
|
||||
"redacted": true,
|
||||
"statusCategory": "success",
|
||||
"httpStatus": 200,
|
||||
"contentType": "application/json",
|
||||
"latencyBucket": "<250ms",
|
||||
"payload": {
|
||||
"status": "error",
|
||||
"message": "Call volume control is not exposed by ModemManager on this backend"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"upstreamBaseline": "58e2204",
|
||||
"capturedAt": "2026-07-15",
|
||||
"sourceInstanceAlias": "instance-2",
|
||||
"operationId": "getCalls",
|
||||
"method": "GET",
|
||||
"pathTemplate": "/api/calls",
|
||||
"redacted": true,
|
||||
"statusCategory": "success",
|
||||
"httpStatus": 200,
|
||||
"contentType": "application/json",
|
||||
"latencyBucket": ">3s",
|
||||
"payload": {
|
||||
"status": "ok",
|
||||
"message": "Success",
|
||||
"data": {
|
||||
"calls": []
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"upstreamBaseline": "58e2204",
|
||||
"capturedAt": "2026-07-15",
|
||||
"sourceInstanceAlias": "instance-2",
|
||||
"operationId": "getImsStatus",
|
||||
"method": "GET",
|
||||
"pathTemplate": "/api/ims/status",
|
||||
"redacted": true,
|
||||
"statusCategory": "success",
|
||||
"httpStatus": 200,
|
||||
"contentType": "application/json",
|
||||
"latencyBucket": "<250ms",
|
||||
"payload": {
|
||||
"status": "error",
|
||||
"message": "IMS status is not exposed by ModemManager on this backend"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"upstreamBaseline": "58e2204",
|
||||
"capturedAt": "2026-07-15",
|
||||
"sourceInstanceAlias": "instance-2",
|
||||
"operationId": "getVoicemailStatus",
|
||||
"method": "GET",
|
||||
"pathTemplate": "/api/voicemail/status",
|
||||
"redacted": true,
|
||||
"statusCategory": "success",
|
||||
"httpStatus": 200,
|
||||
"contentType": "application/json",
|
||||
"latencyBucket": "<250ms",
|
||||
"payload": {
|
||||
"status": "error",
|
||||
"message": "Voicemail status is not exposed by ModemManager on this backend"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"upstreamBaseline": "58e2204",
|
||||
"capturedAt": "2026-07-15",
|
||||
"sourceInstanceAlias": "instance-1",
|
||||
"operationId": "getCells",
|
||||
"method": "GET",
|
||||
"pathTemplate": "/api/cells",
|
||||
"redacted": true,
|
||||
"statusCategory": "success",
|
||||
"httpStatus": 200,
|
||||
"contentType": "application/json",
|
||||
"latencyBucket": "<250ms",
|
||||
"payload": {
|
||||
"status": "ok",
|
||||
"message": "Success",
|
||||
"data": {
|
||||
"serving_cell": {
|
||||
"tech": "lte",
|
||||
"cell_id": 0,
|
||||
"tac": 0
|
||||
},
|
||||
"cells": [
|
||||
{
|
||||
"is_serving": true,
|
||||
"tech": "lte",
|
||||
"cell_id": 0,
|
||||
"band": "B3",
|
||||
"arfcn": "1400",
|
||||
"pci": "[REDACTED]",
|
||||
"rsrp": "-8760",
|
||||
"rsrq": "-870",
|
||||
"sinr": "",
|
||||
"earfcn": "1400",
|
||||
"type": "LTE"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"upstreamBaseline": "58e2204",
|
||||
"capturedAt": "2026-07-15",
|
||||
"sourceInstanceAlias": "instance-1",
|
||||
"operationId": "getLocationCellInfo",
|
||||
"method": "GET",
|
||||
"pathTemplate": "/api/location/cell-info",
|
||||
"redacted": true,
|
||||
"statusCategory": "success",
|
||||
"httpStatus": 200,
|
||||
"contentType": "application/json",
|
||||
"latencyBucket": "<250ms",
|
||||
"payload": {
|
||||
"status": "ok",
|
||||
"message": "Success",
|
||||
"data": {
|
||||
"available": true,
|
||||
"cell_info": {
|
||||
"mcc": "460",
|
||||
"mnc": "00",
|
||||
"lac": 0,
|
||||
"cid": 0,
|
||||
"signal_strength": -88,
|
||||
"radio_type": "LTE",
|
||||
"arfcn": 1400,
|
||||
"pci": 0,
|
||||
"rsrq": -8.7
|
||||
},
|
||||
"neighbor_cells": [],
|
||||
"cells": [
|
||||
{
|
||||
"mcc": "460",
|
||||
"mnc": "00",
|
||||
"lac": 0,
|
||||
"cid": 0,
|
||||
"signal_strength": -88,
|
||||
"radio_type": "LTE",
|
||||
"arfcn": 1400,
|
||||
"pci": 0,
|
||||
"rsrq": -8.7
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"upstreamBaseline": "58e2204",
|
||||
"capturedAt": "2026-07-15",
|
||||
"sourceInstanceAlias": "instance-1",
|
||||
"operationId": "getNetwork",
|
||||
"method": "GET",
|
||||
"pathTemplate": "/api/network",
|
||||
"redacted": true,
|
||||
"statusCategory": "success",
|
||||
"httpStatus": 200,
|
||||
"contentType": "application/json",
|
||||
"latencyBucket": "<250ms",
|
||||
"payload": {
|
||||
"status": "ok",
|
||||
"message": "Success",
|
||||
"data": {
|
||||
"operator_name": "中国移动",
|
||||
"registration_status": "registered",
|
||||
"technology_preference": "lte-advanced",
|
||||
"signal_strength": 80,
|
||||
"mcc": "460",
|
||||
"mnc": "00"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"upstreamBaseline": "58e2204",
|
||||
"capturedAt": "2026-07-15",
|
||||
"sourceInstanceAlias": "instance-1",
|
||||
"operationId": "getNetworkOperators",
|
||||
"method": "GET",
|
||||
"pathTemplate": "/api/network/operators",
|
||||
"redacted": true,
|
||||
"statusCategory": "success",
|
||||
"httpStatus": 200,
|
||||
"contentType": "application/json",
|
||||
"latencyBucket": "<250ms",
|
||||
"payload": {
|
||||
"status": "ok",
|
||||
"message": "Success",
|
||||
"data": {
|
||||
"operators": [
|
||||
{
|
||||
"path": "[REDACTED]",
|
||||
"name": "中国移动",
|
||||
"status": "current",
|
||||
"mcc": "460",
|
||||
"mnc": "00",
|
||||
"technologies": [
|
||||
"LTE-ADVANCED"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"upstreamBaseline": "58e2204",
|
||||
"capturedAt": "2026-07-15",
|
||||
"sourceInstanceAlias": "instance-1",
|
||||
"operationId": "getNetworkSignalStrength",
|
||||
"method": "GET",
|
||||
"pathTemplate": "/api/network/signal-strength",
|
||||
"redacted": true,
|
||||
"statusCategory": "success",
|
||||
"httpStatus": 200,
|
||||
"contentType": "application/json",
|
||||
"latencyBucket": "<250ms",
|
||||
"payload": {
|
||||
"status": "ok",
|
||||
"message": "Success",
|
||||
"data": {
|
||||
"strength": 80
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"upstreamBaseline": "58e2204",
|
||||
"capturedAt": "2026-07-15",
|
||||
"sourceInstanceAlias": "instance-2",
|
||||
"operationId": "getCells",
|
||||
"method": "GET",
|
||||
"pathTemplate": "/api/cells",
|
||||
"redacted": true,
|
||||
"statusCategory": "success",
|
||||
"httpStatus": 200,
|
||||
"contentType": "application/json",
|
||||
"latencyBucket": "<250ms",
|
||||
"payload": {
|
||||
"status": "ok",
|
||||
"message": "Success",
|
||||
"data": {
|
||||
"serving_cell": {
|
||||
"tech": "lte",
|
||||
"cell_id": 0,
|
||||
"tac": 0
|
||||
},
|
||||
"cells": [
|
||||
{
|
||||
"is_serving": true,
|
||||
"tech": "lte",
|
||||
"cell_id": 0,
|
||||
"band": "B3",
|
||||
"arfcn": "1400",
|
||||
"pci": "[REDACTED]",
|
||||
"rsrp": "-9010",
|
||||
"rsrq": "-900",
|
||||
"sinr": "",
|
||||
"earfcn": "1400",
|
||||
"type": "LTE"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"upstreamBaseline": "58e2204",
|
||||
"capturedAt": "2026-07-15",
|
||||
"sourceInstanceAlias": "instance-2",
|
||||
"operationId": "getLocationCellInfo",
|
||||
"method": "GET",
|
||||
"pathTemplate": "/api/location/cell-info",
|
||||
"redacted": true,
|
||||
"statusCategory": "success",
|
||||
"httpStatus": 200,
|
||||
"contentType": "application/json",
|
||||
"latencyBucket": "<250ms",
|
||||
"payload": {
|
||||
"status": "ok",
|
||||
"message": "Success",
|
||||
"data": {
|
||||
"available": true,
|
||||
"cell_info": {
|
||||
"mcc": "460",
|
||||
"mnc": "00",
|
||||
"lac": 0,
|
||||
"cid": 0,
|
||||
"signal_strength": -90,
|
||||
"radio_type": "LTE",
|
||||
"arfcn": 1400,
|
||||
"pci": 0,
|
||||
"rsrq": -8.9
|
||||
},
|
||||
"neighbor_cells": [],
|
||||
"cells": [
|
||||
{
|
||||
"mcc": "460",
|
||||
"mnc": "00",
|
||||
"lac": 0,
|
||||
"cid": 0,
|
||||
"signal_strength": -90,
|
||||
"radio_type": "LTE",
|
||||
"arfcn": 1400,
|
||||
"pci": 0,
|
||||
"rsrq": -8.9
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"upstreamBaseline": "58e2204",
|
||||
"capturedAt": "2026-07-15",
|
||||
"sourceInstanceAlias": "instance-2",
|
||||
"operationId": "getNetwork",
|
||||
"method": "GET",
|
||||
"pathTemplate": "/api/network",
|
||||
"redacted": true,
|
||||
"statusCategory": "success",
|
||||
"httpStatus": 200,
|
||||
"contentType": "application/json",
|
||||
"latencyBucket": "<250ms",
|
||||
"payload": {
|
||||
"status": "ok",
|
||||
"message": "Success",
|
||||
"data": {
|
||||
"operator_name": "中国移动",
|
||||
"registration_status": "registered",
|
||||
"technology_preference": "lte-advanced",
|
||||
"signal_strength": 76,
|
||||
"mcc": "460",
|
||||
"mnc": "00"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"upstreamBaseline": "58e2204",
|
||||
"capturedAt": "2026-07-15",
|
||||
"sourceInstanceAlias": "instance-2",
|
||||
"operationId": "getNetworkOperators",
|
||||
"method": "GET",
|
||||
"pathTemplate": "/api/network/operators",
|
||||
"redacted": true,
|
||||
"statusCategory": "success",
|
||||
"httpStatus": 200,
|
||||
"contentType": "application/json",
|
||||
"latencyBucket": "<250ms",
|
||||
"payload": {
|
||||
"status": "ok",
|
||||
"message": "Success",
|
||||
"data": {
|
||||
"operators": [
|
||||
{
|
||||
"path": "[REDACTED]",
|
||||
"name": "中国移动",
|
||||
"status": "current",
|
||||
"mcc": "460",
|
||||
"mnc": "00",
|
||||
"technologies": [
|
||||
"LTE-ADVANCED"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"upstreamBaseline": "58e2204",
|
||||
"capturedAt": "2026-07-15",
|
||||
"sourceInstanceAlias": "instance-2",
|
||||
"operationId": "getNetworkSignalStrength",
|
||||
"method": "GET",
|
||||
"pathTemplate": "/api/network/signal-strength",
|
||||
"redacted": true,
|
||||
"statusCategory": "success",
|
||||
"httpStatus": 200,
|
||||
"contentType": "application/json",
|
||||
"latencyBucket": "<250ms",
|
||||
"payload": {
|
||||
"status": "ok",
|
||||
"message": "Success",
|
||||
"data": {
|
||||
"strength": 76
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"upstreamBaseline": "58e2204",
|
||||
"capturedAt": "2026-07-15",
|
||||
"sourceInstanceAlias": "instance-1",
|
||||
"operationId": "getAirplaneMode",
|
||||
"method": "GET",
|
||||
"pathTemplate": "/api/airplane-mode",
|
||||
"redacted": true,
|
||||
"statusCategory": "success",
|
||||
"httpStatus": 200,
|
||||
"contentType": "application/json",
|
||||
"latencyBucket": "<250ms",
|
||||
"payload": {
|
||||
"status": "ok",
|
||||
"message": "Success",
|
||||
"data": {
|
||||
"enabled": false,
|
||||
"powered": true,
|
||||
"online": true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"upstreamBaseline": "58e2204",
|
||||
"capturedAt": "2026-07-15",
|
||||
"sourceInstanceAlias": "instance-1",
|
||||
"operationId": "getApn",
|
||||
"method": "GET",
|
||||
"pathTemplate": "/api/apn",
|
||||
"redacted": true,
|
||||
"statusCategory": "success",
|
||||
"httpStatus": 200,
|
||||
"contentType": "application/json",
|
||||
"latencyBucket": "<250ms",
|
||||
"payload": {
|
||||
"status": "ok",
|
||||
"message": "Success",
|
||||
"data": {
|
||||
"contexts": [
|
||||
{
|
||||
"path": "[REDACTED]",
|
||||
"name": "default",
|
||||
"active": false,
|
||||
"apn": "cmnet",
|
||||
"protocol": "dual",
|
||||
"username": "[REDACTED]",
|
||||
"password": "[REDACTED]",
|
||||
"auth_method": "chap",
|
||||
"context_type": "internet"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"upstreamBaseline": "58e2204",
|
||||
"capturedAt": "2026-07-15",
|
||||
"sourceInstanceAlias": "instance-1",
|
||||
"operationId": "getBasebandRestartStatus",
|
||||
"method": "GET",
|
||||
"pathTemplate": "/api/baseband/restart/status",
|
||||
"redacted": true,
|
||||
"statusCategory": "success",
|
||||
"httpStatus": 200,
|
||||
"contentType": "application/json",
|
||||
"latencyBucket": "<250ms",
|
||||
"payload": {
|
||||
"status": "ok",
|
||||
"message": "Success",
|
||||
"data": {
|
||||
"steps": [],
|
||||
"running": false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"upstreamBaseline": "58e2204",
|
||||
"capturedAt": "2026-07-15",
|
||||
"sourceInstanceAlias": "instance-1",
|
||||
"operationId": "getData",
|
||||
"method": "GET",
|
||||
"pathTemplate": "/api/data",
|
||||
"redacted": true,
|
||||
"statusCategory": "success",
|
||||
"httpStatus": 200,
|
||||
"contentType": "application/json",
|
||||
"latencyBucket": "<250ms",
|
||||
"payload": {
|
||||
"status": "ok",
|
||||
"message": "Success",
|
||||
"data": {
|
||||
"active": false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"upstreamBaseline": "58e2204",
|
||||
"capturedAt": "2026-07-15",
|
||||
"sourceInstanceAlias": "instance-1",
|
||||
"operationId": "getRoaming",
|
||||
"method": "GET",
|
||||
"pathTemplate": "/api/roaming",
|
||||
"redacted": true,
|
||||
"statusCategory": "success",
|
||||
"httpStatus": 200,
|
||||
"contentType": "application/json",
|
||||
"latencyBucket": "<250ms",
|
||||
"payload": {
|
||||
"status": "ok",
|
||||
"message": "Success",
|
||||
"data": {
|
||||
"roaming_allowed": false,
|
||||
"is_roaming": false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"upstreamBaseline": "58e2204",
|
||||
"capturedAt": "2026-07-15",
|
||||
"sourceInstanceAlias": "instance-2",
|
||||
"operationId": "getAirplaneMode",
|
||||
"method": "GET",
|
||||
"pathTemplate": "/api/airplane-mode",
|
||||
"redacted": true,
|
||||
"statusCategory": "success",
|
||||
"httpStatus": 200,
|
||||
"contentType": "application/json",
|
||||
"latencyBucket": "<250ms",
|
||||
"payload": {
|
||||
"status": "ok",
|
||||
"message": "Success",
|
||||
"data": {
|
||||
"enabled": false,
|
||||
"powered": true,
|
||||
"online": true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"upstreamBaseline": "58e2204",
|
||||
"capturedAt": "2026-07-15",
|
||||
"sourceInstanceAlias": "instance-2",
|
||||
"operationId": "getApn",
|
||||
"method": "GET",
|
||||
"pathTemplate": "/api/apn",
|
||||
"redacted": true,
|
||||
"statusCategory": "success",
|
||||
"httpStatus": 200,
|
||||
"contentType": "application/json",
|
||||
"latencyBucket": "<250ms",
|
||||
"payload": {
|
||||
"status": "ok",
|
||||
"message": "Success",
|
||||
"data": {
|
||||
"contexts": [
|
||||
{
|
||||
"path": "[REDACTED]",
|
||||
"name": "default",
|
||||
"active": false,
|
||||
"apn": "cmnet",
|
||||
"protocol": "dual",
|
||||
"username": "[REDACTED]",
|
||||
"password": "[REDACTED]",
|
||||
"auth_method": "chap",
|
||||
"context_type": "internet"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"upstreamBaseline": "58e2204",
|
||||
"capturedAt": "2026-07-15",
|
||||
"sourceInstanceAlias": "instance-2",
|
||||
"operationId": "getBasebandRestartStatus",
|
||||
"method": "GET",
|
||||
"pathTemplate": "/api/baseband/restart/status",
|
||||
"redacted": true,
|
||||
"statusCategory": "success",
|
||||
"httpStatus": 200,
|
||||
"contentType": "application/json",
|
||||
"latencyBucket": "<250ms",
|
||||
"payload": {
|
||||
"status": "ok",
|
||||
"message": "Success",
|
||||
"data": {
|
||||
"steps": [],
|
||||
"running": false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"upstreamBaseline": "58e2204",
|
||||
"capturedAt": "2026-07-15",
|
||||
"sourceInstanceAlias": "instance-2",
|
||||
"operationId": "getData",
|
||||
"method": "GET",
|
||||
"pathTemplate": "/api/data",
|
||||
"redacted": true,
|
||||
"statusCategory": "success",
|
||||
"httpStatus": 200,
|
||||
"contentType": "application/json",
|
||||
"latencyBucket": "<250ms",
|
||||
"payload": {
|
||||
"status": "ok",
|
||||
"message": "Success",
|
||||
"data": {
|
||||
"active": false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"upstreamBaseline": "58e2204",
|
||||
"capturedAt": "2026-07-15",
|
||||
"sourceInstanceAlias": "instance-2",
|
||||
"operationId": "getRoaming",
|
||||
"method": "GET",
|
||||
"pathTemplate": "/api/roaming",
|
||||
"redacted": true,
|
||||
"statusCategory": "success",
|
||||
"httpStatus": 200,
|
||||
"contentType": "application/json",
|
||||
"latencyBucket": "<250ms",
|
||||
"payload": {
|
||||
"status": "ok",
|
||||
"message": "Success",
|
||||
"data": {
|
||||
"roaming_allowed": false,
|
||||
"is_roaming": false
|
||||
}
|
||||
}
|
||||
}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"upstreamBaseline": "58e2204",
|
||||
"capturedAt": "2026-07-15",
|
||||
"sourceInstanceAlias": "instance-1",
|
||||
"operationId": "getDeviceNetworkDdnsConfig",
|
||||
"method": "GET",
|
||||
"pathTemplate": "/api/device-network/ddns/config",
|
||||
"redacted": true,
|
||||
"statusCategory": "success",
|
||||
"httpStatus": 200,
|
||||
"contentType": "application/json",
|
||||
"latencyBucket": "<250ms",
|
||||
"payload": {
|
||||
"status": "ok",
|
||||
"message": "Success",
|
||||
"data": {
|
||||
"access_id": "[REDACTED]",
|
||||
"access_secret": "[REDACTED]",
|
||||
"access_secret_set": false,
|
||||
"enabled": false,
|
||||
"interval_seconds": 300,
|
||||
"ipv4": {
|
||||
"domains": [],
|
||||
"enabled": true,
|
||||
"get_type": "interface",
|
||||
"interface_name": "",
|
||||
"urls": [
|
||||
"[REDACTED]",
|
||||
"[REDACTED]",
|
||||
"[REDACTED]",
|
||||
"[REDACTED]",
|
||||
"[REDACTED]"
|
||||
]
|
||||
},
|
||||
"ipv6": {
|
||||
"domains": [],
|
||||
"enabled": false,
|
||||
"get_type": "interface",
|
||||
"interface_name": "",
|
||||
"urls": [
|
||||
"[REDACTED]",
|
||||
"[REDACTED]",
|
||||
"[REDACTED]",
|
||||
"[REDACTED]",
|
||||
"[REDACTED]"
|
||||
]
|
||||
},
|
||||
"provider": "tencentcloud",
|
||||
"ttl": 600
|
||||
}
|
||||
}
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"upstreamBaseline": "58e2204",
|
||||
"capturedAt": "2026-07-15",
|
||||
"sourceInstanceAlias": "instance-1",
|
||||
"operationId": "getDeviceNetworkDdnsStatus",
|
||||
"method": "GET",
|
||||
"pathTemplate": "/api/device-network/ddns/status",
|
||||
"redacted": true,
|
||||
"statusCategory": "success",
|
||||
"httpStatus": 200,
|
||||
"contentType": "application/json",
|
||||
"latencyBucket": "<250ms",
|
||||
"payload": {
|
||||
"status": "ok",
|
||||
"message": "Success",
|
||||
"data": {
|
||||
"enabled": false,
|
||||
"running": false,
|
||||
"provider": "tencentcloud",
|
||||
"last_sync_at": null,
|
||||
"last_ipv4": null,
|
||||
"last_ipv6": null,
|
||||
"last_message": null
|
||||
}
|
||||
}
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"upstreamBaseline": "58e2204",
|
||||
"capturedAt": "2026-07-15",
|
||||
"sourceInstanceAlias": "instance-1",
|
||||
"operationId": "getDeviceNetworkWlanProfiles",
|
||||
"method": "GET",
|
||||
"pathTemplate": "/api/device-network/wlan/profiles",
|
||||
"redacted": true,
|
||||
"statusCategory": "success",
|
||||
"httpStatus": 200,
|
||||
"contentType": "application/json",
|
||||
"latencyBucket": "250ms-1s",
|
||||
"payload": {
|
||||
"status": "ok",
|
||||
"message": "Success",
|
||||
"data": {
|
||||
"profiles": [
|
||||
{
|
||||
"id": "xiaomi",
|
||||
"uuid": "[REDACTED]",
|
||||
"ssid": "[REDACTED]",
|
||||
"interface_name": "wlan0",
|
||||
"active": true,
|
||||
"auto_join": true
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"upstreamBaseline": "58e2204",
|
||||
"capturedAt": "2026-07-15",
|
||||
"sourceInstanceAlias": "instance-1",
|
||||
"operationId": "getDeviceNetworkWlanStatus",
|
||||
"method": "GET",
|
||||
"pathTemplate": "/api/device-network/wlan/status",
|
||||
"redacted": true,
|
||||
"statusCategory": "success",
|
||||
"httpStatus": 200,
|
||||
"contentType": "application/json",
|
||||
"latencyBucket": "250ms-1s",
|
||||
"payload": {
|
||||
"status": "ok",
|
||||
"message": "Success",
|
||||
"data": {
|
||||
"available": true,
|
||||
"enabled": true,
|
||||
"hardware_enabled": true,
|
||||
"interface_name": "wlan0",
|
||||
"connected": true,
|
||||
"ssid": "[REDACTED]",
|
||||
"connection_id": "xiaomi",
|
||||
"ipv4_addresses": [
|
||||
"[REDACTED]"
|
||||
],
|
||||
"ipv4_gateway": "[REDACTED]",
|
||||
"ipv6_addresses": [
|
||||
"[REDACTED]",
|
||||
"[REDACTED]",
|
||||
"[REDACTED]"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"upstreamBaseline": "58e2204",
|
||||
"capturedAt": "2026-07-15",
|
||||
"sourceInstanceAlias": "instance-1",
|
||||
"operationId": "getNetworkConnectionAddresses",
|
||||
"method": "GET",
|
||||
"pathTemplate": "/api/network/connection-addresses",
|
||||
"redacted": true,
|
||||
"statusCategory": "success",
|
||||
"httpStatus": 200,
|
||||
"contentType": "application/json",
|
||||
"latencyBucket": "<250ms",
|
||||
"payload": {
|
||||
"status": "ok",
|
||||
"message": "Success",
|
||||
"data": {
|
||||
"ipv4": [
|
||||
"[REDACTED]"
|
||||
],
|
||||
"ipv6": [
|
||||
"[REDACTED]",
|
||||
"[REDACTED]"
|
||||
],
|
||||
"ipv4_interface": "wlan0",
|
||||
"ipv6_interface": "wlan0"
|
||||
}
|
||||
}
|
||||
}
|
||||
+273
@@ -0,0 +1,273 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"upstreamBaseline": "58e2204",
|
||||
"capturedAt": "2026-07-15",
|
||||
"sourceInstanceAlias": "instance-1",
|
||||
"operationId": "getNetworkInterfaces",
|
||||
"method": "GET",
|
||||
"pathTemplate": "/api/network/interfaces",
|
||||
"redacted": true,
|
||||
"statusCategory": "success",
|
||||
"httpStatus": 200,
|
||||
"contentType": "application/json",
|
||||
"latencyBucket": "<250ms",
|
||||
"payload": {
|
||||
"status": "ok",
|
||||
"message": "Success",
|
||||
"data": {
|
||||
"interfaces": [
|
||||
{
|
||||
"name": "lo",
|
||||
"status": "up",
|
||||
"is_wireless": false,
|
||||
"is_cellular": false,
|
||||
"is_default_ipv4": false,
|
||||
"is_default_ipv6": false,
|
||||
"mtu": 65536,
|
||||
"ip_addresses": [
|
||||
{
|
||||
"address": "[REDACTED]",
|
||||
"prefix_len": 0,
|
||||
"ip_type": "[REDACTED]",
|
||||
"scope": "[REDACTED]"
|
||||
},
|
||||
{
|
||||
"address": "[REDACTED]",
|
||||
"prefix_len": 0,
|
||||
"ip_type": "[REDACTED]",
|
||||
"scope": "[REDACTED]"
|
||||
}
|
||||
],
|
||||
"rx_bytes": 0,
|
||||
"tx_bytes": 0,
|
||||
"rx_packets": 0,
|
||||
"tx_packets": 0,
|
||||
"rx_errors": 0,
|
||||
"tx_errors": 0
|
||||
},
|
||||
{
|
||||
"name": "pan0",
|
||||
"status": "down",
|
||||
"is_wireless": false,
|
||||
"is_cellular": false,
|
||||
"is_default_ipv4": false,
|
||||
"is_default_ipv6": false,
|
||||
"mac_address": "[REDACTED]",
|
||||
"mtu": 1500,
|
||||
"ip_addresses": [],
|
||||
"rx_bytes": 0,
|
||||
"tx_bytes": 0,
|
||||
"rx_packets": 0,
|
||||
"tx_packets": 0,
|
||||
"rx_errors": 0,
|
||||
"tx_errors": 0
|
||||
},
|
||||
{
|
||||
"name": "usb0",
|
||||
"status": "down",
|
||||
"is_wireless": false,
|
||||
"is_cellular": false,
|
||||
"is_default_ipv4": false,
|
||||
"is_default_ipv6": false,
|
||||
"mac_address": "[REDACTED]",
|
||||
"mtu": 1500,
|
||||
"ip_addresses": [],
|
||||
"rx_bytes": 0,
|
||||
"tx_bytes": 0,
|
||||
"rx_packets": 0,
|
||||
"tx_packets": 0,
|
||||
"rx_errors": 0,
|
||||
"tx_errors": 0
|
||||
},
|
||||
{
|
||||
"name": "usb1",
|
||||
"status": "down",
|
||||
"is_wireless": false,
|
||||
"is_cellular": false,
|
||||
"is_default_ipv4": false,
|
||||
"is_default_ipv6": false,
|
||||
"mac_address": "[REDACTED]",
|
||||
"mtu": 1500,
|
||||
"ip_addresses": [],
|
||||
"rx_bytes": 0,
|
||||
"tx_bytes": 0,
|
||||
"rx_packets": 0,
|
||||
"tx_packets": 0,
|
||||
"rx_errors": 0,
|
||||
"tx_errors": 0
|
||||
},
|
||||
{
|
||||
"name": "wlan0",
|
||||
"status": "up",
|
||||
"is_wireless": true,
|
||||
"is_cellular": false,
|
||||
"is_default_ipv4": true,
|
||||
"is_default_ipv6": true,
|
||||
"mac_address": "[REDACTED]",
|
||||
"mtu": 1500,
|
||||
"ip_addresses": [
|
||||
{
|
||||
"address": "[REDACTED]",
|
||||
"prefix_len": 0,
|
||||
"ip_type": "[REDACTED]",
|
||||
"scope": "[REDACTED]"
|
||||
},
|
||||
{
|
||||
"address": "[REDACTED]",
|
||||
"prefix_len": 0,
|
||||
"ip_type": "[REDACTED]",
|
||||
"scope": "[REDACTED]"
|
||||
},
|
||||
{
|
||||
"address": "[REDACTED]",
|
||||
"prefix_len": 0,
|
||||
"ip_type": "[REDACTED]",
|
||||
"scope": "[REDACTED]"
|
||||
},
|
||||
{
|
||||
"address": "[REDACTED]",
|
||||
"prefix_len": 0,
|
||||
"ip_type": "[REDACTED]",
|
||||
"scope": "[REDACTED]"
|
||||
}
|
||||
],
|
||||
"rx_bytes": 0,
|
||||
"tx_bytes": 0,
|
||||
"rx_packets": 0,
|
||||
"tx_packets": 0,
|
||||
"rx_errors": 0,
|
||||
"tx_errors": 0
|
||||
},
|
||||
{
|
||||
"name": "wwan0",
|
||||
"status": "down",
|
||||
"is_wireless": false,
|
||||
"is_cellular": false,
|
||||
"is_default_ipv4": false,
|
||||
"is_default_ipv6": false,
|
||||
"mtu": 1500,
|
||||
"ip_addresses": [],
|
||||
"rx_bytes": 0,
|
||||
"tx_bytes": 0,
|
||||
"rx_packets": 0,
|
||||
"tx_packets": 0,
|
||||
"rx_errors": 0,
|
||||
"tx_errors": 0
|
||||
},
|
||||
{
|
||||
"name": "wwan1",
|
||||
"status": "down",
|
||||
"is_wireless": false,
|
||||
"is_cellular": false,
|
||||
"is_default_ipv4": false,
|
||||
"is_default_ipv6": false,
|
||||
"mtu": 1500,
|
||||
"ip_addresses": [],
|
||||
"rx_bytes": 0,
|
||||
"tx_bytes": 0,
|
||||
"rx_packets": 0,
|
||||
"tx_packets": 0,
|
||||
"rx_errors": 0,
|
||||
"tx_errors": 0
|
||||
},
|
||||
{
|
||||
"name": "wwan2",
|
||||
"status": "down",
|
||||
"is_wireless": false,
|
||||
"is_cellular": false,
|
||||
"is_default_ipv4": false,
|
||||
"is_default_ipv6": false,
|
||||
"mtu": 1500,
|
||||
"ip_addresses": [],
|
||||
"rx_bytes": 0,
|
||||
"tx_bytes": 0,
|
||||
"rx_packets": 0,
|
||||
"tx_packets": 0,
|
||||
"rx_errors": 0,
|
||||
"tx_errors": 0
|
||||
},
|
||||
{
|
||||
"name": "wwan3",
|
||||
"status": "down",
|
||||
"is_wireless": false,
|
||||
"is_cellular": false,
|
||||
"is_default_ipv4": false,
|
||||
"is_default_ipv6": false,
|
||||
"mtu": 1500,
|
||||
"ip_addresses": [],
|
||||
"rx_bytes": 0,
|
||||
"tx_bytes": 0,
|
||||
"rx_packets": 0,
|
||||
"tx_packets": 0,
|
||||
"rx_errors": 0,
|
||||
"tx_errors": 0
|
||||
},
|
||||
{
|
||||
"name": "wwan4",
|
||||
"status": "down",
|
||||
"is_wireless": false,
|
||||
"is_cellular": false,
|
||||
"is_default_ipv4": false,
|
||||
"is_default_ipv6": false,
|
||||
"mtu": 1500,
|
||||
"ip_addresses": [],
|
||||
"rx_bytes": 0,
|
||||
"tx_bytes": 0,
|
||||
"rx_packets": 0,
|
||||
"tx_packets": 0,
|
||||
"rx_errors": 0,
|
||||
"tx_errors": 0
|
||||
},
|
||||
{
|
||||
"name": "wwan5",
|
||||
"status": "down",
|
||||
"is_wireless": false,
|
||||
"is_cellular": false,
|
||||
"is_default_ipv4": false,
|
||||
"is_default_ipv6": false,
|
||||
"mtu": 1500,
|
||||
"ip_addresses": [],
|
||||
"rx_bytes": 0,
|
||||
"tx_bytes": 0,
|
||||
"rx_packets": 0,
|
||||
"tx_packets": 0,
|
||||
"rx_errors": 0,
|
||||
"tx_errors": 0
|
||||
},
|
||||
{
|
||||
"name": "wwan6",
|
||||
"status": "down",
|
||||
"is_wireless": false,
|
||||
"is_cellular": false,
|
||||
"is_default_ipv4": false,
|
||||
"is_default_ipv6": false,
|
||||
"mtu": 1500,
|
||||
"ip_addresses": [],
|
||||
"rx_bytes": 0,
|
||||
"tx_bytes": 0,
|
||||
"rx_packets": 0,
|
||||
"tx_packets": 0,
|
||||
"rx_errors": 0,
|
||||
"tx_errors": 0
|
||||
},
|
||||
{
|
||||
"name": "wwan7",
|
||||
"status": "down",
|
||||
"is_wireless": false,
|
||||
"is_cellular": false,
|
||||
"is_default_ipv4": false,
|
||||
"is_default_ipv6": false,
|
||||
"mtu": 1500,
|
||||
"ip_addresses": [],
|
||||
"rx_bytes": 0,
|
||||
"tx_bytes": 0,
|
||||
"rx_packets": 0,
|
||||
"tx_packets": 0,
|
||||
"rx_errors": 0,
|
||||
"tx_errors": 0
|
||||
}
|
||||
],
|
||||
"total_count": 13
|
||||
}
|
||||
}
|
||||
}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"upstreamBaseline": "58e2204",
|
||||
"capturedAt": "2026-07-15",
|
||||
"sourceInstanceAlias": "instance-2",
|
||||
"operationId": "getDeviceNetworkDdnsConfig",
|
||||
"method": "GET",
|
||||
"pathTemplate": "/api/device-network/ddns/config",
|
||||
"redacted": true,
|
||||
"statusCategory": "success",
|
||||
"httpStatus": 200,
|
||||
"contentType": "application/json",
|
||||
"latencyBucket": "<250ms",
|
||||
"payload": {
|
||||
"status": "ok",
|
||||
"message": "Success",
|
||||
"data": {
|
||||
"access_id": "[REDACTED]",
|
||||
"access_secret": "[REDACTED]",
|
||||
"access_secret_set": false,
|
||||
"enabled": false,
|
||||
"interval_seconds": 300,
|
||||
"ipv4": {
|
||||
"domains": [],
|
||||
"enabled": true,
|
||||
"get_type": "interface",
|
||||
"interface_name": "",
|
||||
"urls": [
|
||||
"[REDACTED]",
|
||||
"[REDACTED]",
|
||||
"[REDACTED]",
|
||||
"[REDACTED]",
|
||||
"[REDACTED]"
|
||||
]
|
||||
},
|
||||
"ipv6": {
|
||||
"domains": [],
|
||||
"enabled": false,
|
||||
"get_type": "interface",
|
||||
"interface_name": "",
|
||||
"urls": [
|
||||
"[REDACTED]",
|
||||
"[REDACTED]",
|
||||
"[REDACTED]",
|
||||
"[REDACTED]",
|
||||
"[REDACTED]"
|
||||
]
|
||||
},
|
||||
"provider": "tencentcloud",
|
||||
"ttl": 600
|
||||
}
|
||||
}
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"upstreamBaseline": "58e2204",
|
||||
"capturedAt": "2026-07-15",
|
||||
"sourceInstanceAlias": "instance-2",
|
||||
"operationId": "getDeviceNetworkDdnsStatus",
|
||||
"method": "GET",
|
||||
"pathTemplate": "/api/device-network/ddns/status",
|
||||
"redacted": true,
|
||||
"statusCategory": "success",
|
||||
"httpStatus": 200,
|
||||
"contentType": "application/json",
|
||||
"latencyBucket": "<250ms",
|
||||
"payload": {
|
||||
"status": "ok",
|
||||
"message": "Success",
|
||||
"data": {
|
||||
"enabled": false,
|
||||
"running": false,
|
||||
"provider": "tencentcloud",
|
||||
"last_sync_at": null,
|
||||
"last_ipv4": null,
|
||||
"last_ipv6": null,
|
||||
"last_message": null
|
||||
}
|
||||
}
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"upstreamBaseline": "58e2204",
|
||||
"capturedAt": "2026-07-15",
|
||||
"sourceInstanceAlias": "instance-2",
|
||||
"operationId": "getDeviceNetworkWlanProfiles",
|
||||
"method": "GET",
|
||||
"pathTemplate": "/api/device-network/wlan/profiles",
|
||||
"redacted": true,
|
||||
"statusCategory": "success",
|
||||
"httpStatus": 200,
|
||||
"contentType": "application/json",
|
||||
"latencyBucket": "<250ms",
|
||||
"payload": {
|
||||
"status": "ok",
|
||||
"message": "Success",
|
||||
"data": {
|
||||
"profiles": [
|
||||
{
|
||||
"id": "xiaomi",
|
||||
"uuid": "[REDACTED]",
|
||||
"ssid": "[REDACTED]",
|
||||
"interface_name": "wlan0",
|
||||
"active": true,
|
||||
"auto_join": true
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"upstreamBaseline": "58e2204",
|
||||
"capturedAt": "2026-07-15",
|
||||
"sourceInstanceAlias": "instance-2",
|
||||
"operationId": "getDeviceNetworkWlanStatus",
|
||||
"method": "GET",
|
||||
"pathTemplate": "/api/device-network/wlan/status",
|
||||
"redacted": true,
|
||||
"statusCategory": "success",
|
||||
"httpStatus": 200,
|
||||
"contentType": "application/json",
|
||||
"latencyBucket": "250ms-1s",
|
||||
"payload": {
|
||||
"status": "ok",
|
||||
"message": "Success",
|
||||
"data": {
|
||||
"available": true,
|
||||
"enabled": true,
|
||||
"hardware_enabled": true,
|
||||
"interface_name": "wlan0",
|
||||
"connected": true,
|
||||
"ssid": "[REDACTED]",
|
||||
"connection_id": "xiaomi",
|
||||
"ipv4_addresses": [
|
||||
"[REDACTED]"
|
||||
],
|
||||
"ipv4_gateway": "[REDACTED]",
|
||||
"ipv6_addresses": [
|
||||
"[REDACTED]",
|
||||
"[REDACTED]",
|
||||
"[REDACTED]"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"upstreamBaseline": "58e2204",
|
||||
"capturedAt": "2026-07-15",
|
||||
"sourceInstanceAlias": "instance-2",
|
||||
"operationId": "getNetworkConnectionAddresses",
|
||||
"method": "GET",
|
||||
"pathTemplate": "/api/network/connection-addresses",
|
||||
"redacted": true,
|
||||
"statusCategory": "success",
|
||||
"httpStatus": 200,
|
||||
"contentType": "application/json",
|
||||
"latencyBucket": "<250ms",
|
||||
"payload": {
|
||||
"status": "ok",
|
||||
"message": "Success",
|
||||
"data": {
|
||||
"ipv4": [
|
||||
"[REDACTED]"
|
||||
],
|
||||
"ipv6": [
|
||||
"[REDACTED]",
|
||||
"[REDACTED]"
|
||||
],
|
||||
"ipv4_interface": "wlan0",
|
||||
"ipv6_interface": "wlan0"
|
||||
}
|
||||
}
|
||||
}
|
||||
+273
@@ -0,0 +1,273 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"upstreamBaseline": "58e2204",
|
||||
"capturedAt": "2026-07-15",
|
||||
"sourceInstanceAlias": "instance-2",
|
||||
"operationId": "getNetworkInterfaces",
|
||||
"method": "GET",
|
||||
"pathTemplate": "/api/network/interfaces",
|
||||
"redacted": true,
|
||||
"statusCategory": "success",
|
||||
"httpStatus": 200,
|
||||
"contentType": "application/json",
|
||||
"latencyBucket": "<250ms",
|
||||
"payload": {
|
||||
"status": "ok",
|
||||
"message": "Success",
|
||||
"data": {
|
||||
"interfaces": [
|
||||
{
|
||||
"name": "lo",
|
||||
"status": "up",
|
||||
"is_wireless": false,
|
||||
"is_cellular": false,
|
||||
"is_default_ipv4": false,
|
||||
"is_default_ipv6": false,
|
||||
"mtu": 65536,
|
||||
"ip_addresses": [
|
||||
{
|
||||
"address": "[REDACTED]",
|
||||
"prefix_len": 0,
|
||||
"ip_type": "[REDACTED]",
|
||||
"scope": "[REDACTED]"
|
||||
},
|
||||
{
|
||||
"address": "[REDACTED]",
|
||||
"prefix_len": 0,
|
||||
"ip_type": "[REDACTED]",
|
||||
"scope": "[REDACTED]"
|
||||
}
|
||||
],
|
||||
"rx_bytes": 0,
|
||||
"tx_bytes": 0,
|
||||
"rx_packets": 0,
|
||||
"tx_packets": 0,
|
||||
"rx_errors": 0,
|
||||
"tx_errors": 0
|
||||
},
|
||||
{
|
||||
"name": "pan0",
|
||||
"status": "down",
|
||||
"is_wireless": false,
|
||||
"is_cellular": false,
|
||||
"is_default_ipv4": false,
|
||||
"is_default_ipv6": false,
|
||||
"mac_address": "[REDACTED]",
|
||||
"mtu": 1500,
|
||||
"ip_addresses": [],
|
||||
"rx_bytes": 0,
|
||||
"tx_bytes": 0,
|
||||
"rx_packets": 0,
|
||||
"tx_packets": 0,
|
||||
"rx_errors": 0,
|
||||
"tx_errors": 0
|
||||
},
|
||||
{
|
||||
"name": "usb0",
|
||||
"status": "down",
|
||||
"is_wireless": false,
|
||||
"is_cellular": false,
|
||||
"is_default_ipv4": false,
|
||||
"is_default_ipv6": false,
|
||||
"mac_address": "[REDACTED]",
|
||||
"mtu": 1500,
|
||||
"ip_addresses": [],
|
||||
"rx_bytes": 0,
|
||||
"tx_bytes": 0,
|
||||
"rx_packets": 0,
|
||||
"tx_packets": 0,
|
||||
"rx_errors": 0,
|
||||
"tx_errors": 0
|
||||
},
|
||||
{
|
||||
"name": "usb1",
|
||||
"status": "down",
|
||||
"is_wireless": false,
|
||||
"is_cellular": false,
|
||||
"is_default_ipv4": false,
|
||||
"is_default_ipv6": false,
|
||||
"mac_address": "[REDACTED]",
|
||||
"mtu": 1500,
|
||||
"ip_addresses": [],
|
||||
"rx_bytes": 0,
|
||||
"tx_bytes": 0,
|
||||
"rx_packets": 0,
|
||||
"tx_packets": 0,
|
||||
"rx_errors": 0,
|
||||
"tx_errors": 0
|
||||
},
|
||||
{
|
||||
"name": "wlan0",
|
||||
"status": "up",
|
||||
"is_wireless": true,
|
||||
"is_cellular": false,
|
||||
"is_default_ipv4": true,
|
||||
"is_default_ipv6": true,
|
||||
"mac_address": "[REDACTED]",
|
||||
"mtu": 1500,
|
||||
"ip_addresses": [
|
||||
{
|
||||
"address": "[REDACTED]",
|
||||
"prefix_len": 0,
|
||||
"ip_type": "[REDACTED]",
|
||||
"scope": "[REDACTED]"
|
||||
},
|
||||
{
|
||||
"address": "[REDACTED]",
|
||||
"prefix_len": 0,
|
||||
"ip_type": "[REDACTED]",
|
||||
"scope": "[REDACTED]"
|
||||
},
|
||||
{
|
||||
"address": "[REDACTED]",
|
||||
"prefix_len": 0,
|
||||
"ip_type": "[REDACTED]",
|
||||
"scope": "[REDACTED]"
|
||||
},
|
||||
{
|
||||
"address": "[REDACTED]",
|
||||
"prefix_len": 0,
|
||||
"ip_type": "[REDACTED]",
|
||||
"scope": "[REDACTED]"
|
||||
}
|
||||
],
|
||||
"rx_bytes": 0,
|
||||
"tx_bytes": 0,
|
||||
"rx_packets": 0,
|
||||
"tx_packets": 0,
|
||||
"rx_errors": 0,
|
||||
"tx_errors": 0
|
||||
},
|
||||
{
|
||||
"name": "wwan0",
|
||||
"status": "down",
|
||||
"is_wireless": false,
|
||||
"is_cellular": false,
|
||||
"is_default_ipv4": false,
|
||||
"is_default_ipv6": false,
|
||||
"mtu": 1500,
|
||||
"ip_addresses": [],
|
||||
"rx_bytes": 0,
|
||||
"tx_bytes": 0,
|
||||
"rx_packets": 0,
|
||||
"tx_packets": 0,
|
||||
"rx_errors": 0,
|
||||
"tx_errors": 0
|
||||
},
|
||||
{
|
||||
"name": "wwan1",
|
||||
"status": "down",
|
||||
"is_wireless": false,
|
||||
"is_cellular": false,
|
||||
"is_default_ipv4": false,
|
||||
"is_default_ipv6": false,
|
||||
"mtu": 1500,
|
||||
"ip_addresses": [],
|
||||
"rx_bytes": 0,
|
||||
"tx_bytes": 0,
|
||||
"rx_packets": 0,
|
||||
"tx_packets": 0,
|
||||
"rx_errors": 0,
|
||||
"tx_errors": 0
|
||||
},
|
||||
{
|
||||
"name": "wwan2",
|
||||
"status": "down",
|
||||
"is_wireless": false,
|
||||
"is_cellular": false,
|
||||
"is_default_ipv4": false,
|
||||
"is_default_ipv6": false,
|
||||
"mtu": 1500,
|
||||
"ip_addresses": [],
|
||||
"rx_bytes": 0,
|
||||
"tx_bytes": 0,
|
||||
"rx_packets": 0,
|
||||
"tx_packets": 0,
|
||||
"rx_errors": 0,
|
||||
"tx_errors": 0
|
||||
},
|
||||
{
|
||||
"name": "wwan3",
|
||||
"status": "down",
|
||||
"is_wireless": false,
|
||||
"is_cellular": false,
|
||||
"is_default_ipv4": false,
|
||||
"is_default_ipv6": false,
|
||||
"mtu": 1500,
|
||||
"ip_addresses": [],
|
||||
"rx_bytes": 0,
|
||||
"tx_bytes": 0,
|
||||
"rx_packets": 0,
|
||||
"tx_packets": 0,
|
||||
"rx_errors": 0,
|
||||
"tx_errors": 0
|
||||
},
|
||||
{
|
||||
"name": "wwan4",
|
||||
"status": "down",
|
||||
"is_wireless": false,
|
||||
"is_cellular": false,
|
||||
"is_default_ipv4": false,
|
||||
"is_default_ipv6": false,
|
||||
"mtu": 1500,
|
||||
"ip_addresses": [],
|
||||
"rx_bytes": 0,
|
||||
"tx_bytes": 0,
|
||||
"rx_packets": 0,
|
||||
"tx_packets": 0,
|
||||
"rx_errors": 0,
|
||||
"tx_errors": 0
|
||||
},
|
||||
{
|
||||
"name": "wwan5",
|
||||
"status": "down",
|
||||
"is_wireless": false,
|
||||
"is_cellular": false,
|
||||
"is_default_ipv4": false,
|
||||
"is_default_ipv6": false,
|
||||
"mtu": 1500,
|
||||
"ip_addresses": [],
|
||||
"rx_bytes": 0,
|
||||
"tx_bytes": 0,
|
||||
"rx_packets": 0,
|
||||
"tx_packets": 0,
|
||||
"rx_errors": 0,
|
||||
"tx_errors": 0
|
||||
},
|
||||
{
|
||||
"name": "wwan6",
|
||||
"status": "down",
|
||||
"is_wireless": false,
|
||||
"is_cellular": false,
|
||||
"is_default_ipv4": false,
|
||||
"is_default_ipv6": false,
|
||||
"mtu": 1500,
|
||||
"ip_addresses": [],
|
||||
"rx_bytes": 0,
|
||||
"tx_bytes": 0,
|
||||
"rx_packets": 0,
|
||||
"tx_packets": 0,
|
||||
"rx_errors": 0,
|
||||
"tx_errors": 0
|
||||
},
|
||||
{
|
||||
"name": "wwan7",
|
||||
"status": "down",
|
||||
"is_wireless": false,
|
||||
"is_cellular": false,
|
||||
"is_default_ipv4": false,
|
||||
"is_default_ipv6": false,
|
||||
"mtu": 1500,
|
||||
"ip_addresses": [],
|
||||
"rx_bytes": 0,
|
||||
"tx_bytes": 0,
|
||||
"rx_packets": 0,
|
||||
"tx_packets": 0,
|
||||
"rx_errors": 0,
|
||||
"tx_errors": 0
|
||||
}
|
||||
],
|
||||
"total_count": 13
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"upstreamBaseline": "58e2204",
|
||||
"capturedAt": "2026-07-15",
|
||||
"sourceInstanceAlias": "instance-1",
|
||||
"operationId": "getDevice",
|
||||
"method": "GET",
|
||||
"pathTemplate": "/api/device",
|
||||
"redacted": true,
|
||||
"statusCategory": "success",
|
||||
"httpStatus": 200,
|
||||
"contentType": "application/json",
|
||||
"latencyBucket": "250ms-1s",
|
||||
"payload": {
|
||||
"status": "ok",
|
||||
"message": "Success",
|
||||
"data": {
|
||||
"imei": "[REDACTED]",
|
||||
"manufacturer": "QUALCOMM INCORPORATED",
|
||||
"model": "0",
|
||||
"revision": "[REDACTED]",
|
||||
"online": true,
|
||||
"powered": true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"upstreamBaseline": "58e2204",
|
||||
"capturedAt": "2026-07-15",
|
||||
"sourceInstanceAlias": "instance-1",
|
||||
"operationId": "getStats",
|
||||
"method": "GET",
|
||||
"pathTemplate": "/api/stats",
|
||||
"redacted": true,
|
||||
"statusCategory": "success",
|
||||
"httpStatus": 200,
|
||||
"contentType": "application/json",
|
||||
"latencyBucket": "1-3s",
|
||||
"payload": {
|
||||
"status": "ok",
|
||||
"message": "Success",
|
||||
"data": {
|
||||
"network_speed": {
|
||||
"interfaces": [
|
||||
{
|
||||
"interface": "wlan0",
|
||||
"rx_bytes_per_sec": 2325,
|
||||
"tx_bytes_per_sec": 86,
|
||||
"total_rx_bytes": 0,
|
||||
"total_tx_bytes": 0
|
||||
}
|
||||
],
|
||||
"interval_seconds": 1
|
||||
},
|
||||
"memory": {
|
||||
"total_bytes": 0,
|
||||
"available_bytes": 0,
|
||||
"used_bytes": 0,
|
||||
"used_percent": 0,
|
||||
"cached_bytes": 0,
|
||||
"buffers_bytes": 36802560
|
||||
},
|
||||
"disk": [
|
||||
{
|
||||
"mount_point": "/",
|
||||
"fs_type": "ext4",
|
||||
"total_bytes": 0,
|
||||
"used_bytes": 0,
|
||||
"available_bytes": 0,
|
||||
"used_percent": 0
|
||||
},
|
||||
{
|
||||
"mount_point": "/run",
|
||||
"fs_type": "tmpfs",
|
||||
"total_bytes": 38850560,
|
||||
"used_bytes": 1277952,
|
||||
"available_bytes": 37572608,
|
||||
"used_percent": 0
|
||||
}
|
||||
],
|
||||
"cpu_load": {
|
||||
"load_1min": 0.06,
|
||||
"load_5min": 0.05,
|
||||
"load_15min": 0.01,
|
||||
"core_count": 4,
|
||||
"load_percent": 0
|
||||
},
|
||||
"uptime": {
|
||||
"uptime_seconds": 0,
|
||||
"idle_seconds": 0,
|
||||
"uptime_formatted": "[REDACTED]"
|
||||
},
|
||||
"system_info": {
|
||||
"sysname": "Linux",
|
||||
"nodename": "4G-wifi",
|
||||
"release": "[REDACTED]",
|
||||
"version": "[REDACTED]",
|
||||
"machine": "[REDACTED]",
|
||||
"full_info": "[REDACTED]"
|
||||
},
|
||||
"temperature": [
|
||||
{
|
||||
"zone": "[REDACTED]",
|
||||
"type": "[REDACTED]",
|
||||
"temperature": 0
|
||||
},
|
||||
{
|
||||
"zone": "[REDACTED]",
|
||||
"type": "[REDACTED]",
|
||||
"temperature": 0
|
||||
},
|
||||
{
|
||||
"zone": "[REDACTED]",
|
||||
"type": "[REDACTED]",
|
||||
"temperature": 0
|
||||
},
|
||||
{
|
||||
"zone": "[REDACTED]",
|
||||
"type": "[REDACTED]",
|
||||
"temperature": 0
|
||||
},
|
||||
{
|
||||
"zone": "[REDACTED]",
|
||||
"type": "[REDACTED]",
|
||||
"temperature": 0
|
||||
},
|
||||
{
|
||||
"zone": "[REDACTED]",
|
||||
"type": "[REDACTED]",
|
||||
"temperature": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"upstreamBaseline": "58e2204",
|
||||
"capturedAt": "2026-07-15",
|
||||
"sourceInstanceAlias": "instance-1",
|
||||
"operationId": "getStatsCpu",
|
||||
"method": "GET",
|
||||
"pathTemplate": "/api/stats/cpu",
|
||||
"redacted": true,
|
||||
"statusCategory": "success",
|
||||
"httpStatus": 200,
|
||||
"contentType": "application/json",
|
||||
"latencyBucket": "<250ms",
|
||||
"payload": {
|
||||
"status": "ok",
|
||||
"message": "Success",
|
||||
"data": {
|
||||
"core_count": 4,
|
||||
"cores": [
|
||||
{
|
||||
"processor": 0,
|
||||
"bogomips": "38.40",
|
||||
"features": [
|
||||
"fp",
|
||||
"asimd",
|
||||
"evtstrm",
|
||||
"crc32",
|
||||
"cpuid"
|
||||
],
|
||||
"implementer": "0x41",
|
||||
"architecture": "8",
|
||||
"variant": "0x0",
|
||||
"part": "0xd03",
|
||||
"revision": "[REDACTED]"
|
||||
},
|
||||
{
|
||||
"processor": 1,
|
||||
"bogomips": "38.40",
|
||||
"features": [
|
||||
"fp",
|
||||
"asimd",
|
||||
"evtstrm",
|
||||
"crc32",
|
||||
"cpuid"
|
||||
],
|
||||
"implementer": "0x41",
|
||||
"architecture": "8",
|
||||
"variant": "0x0",
|
||||
"part": "0xd03",
|
||||
"revision": "[REDACTED]"
|
||||
},
|
||||
{
|
||||
"processor": 2,
|
||||
"bogomips": "38.40",
|
||||
"features": [
|
||||
"fp",
|
||||
"asimd",
|
||||
"evtstrm",
|
||||
"crc32",
|
||||
"cpuid"
|
||||
],
|
||||
"implementer": "0x41",
|
||||
"architecture": "8",
|
||||
"variant": "0x0",
|
||||
"part": "0xd03",
|
||||
"revision": "[REDACTED]"
|
||||
},
|
||||
{
|
||||
"processor": 3,
|
||||
"bogomips": "38.40",
|
||||
"features": [
|
||||
"fp",
|
||||
"asimd",
|
||||
"evtstrm",
|
||||
"crc32",
|
||||
"cpuid"
|
||||
],
|
||||
"implementer": "0x41",
|
||||
"architecture": "8",
|
||||
"variant": "0x0",
|
||||
"part": "0xd03",
|
||||
"revision": "[REDACTED]"
|
||||
}
|
||||
],
|
||||
"hardware": "",
|
||||
"serial": "[REDACTED]",
|
||||
"model_name": "ARM CPU (part: 0xd03)"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"upstreamBaseline": "58e2204",
|
||||
"capturedAt": "2026-07-15",
|
||||
"sourceInstanceAlias": "instance-2",
|
||||
"operationId": "getDevice",
|
||||
"method": "GET",
|
||||
"pathTemplate": "/api/device",
|
||||
"redacted": true,
|
||||
"statusCategory": "success",
|
||||
"httpStatus": 200,
|
||||
"contentType": "application/json",
|
||||
"latencyBucket": "<250ms",
|
||||
"payload": {
|
||||
"status": "ok",
|
||||
"message": "Success",
|
||||
"data": {
|
||||
"imei": "[REDACTED]",
|
||||
"manufacturer": "QUALCOMM INCORPORATED",
|
||||
"model": "0",
|
||||
"revision": "[REDACTED]",
|
||||
"online": true,
|
||||
"powered": true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"upstreamBaseline": "58e2204",
|
||||
"capturedAt": "2026-07-15",
|
||||
"sourceInstanceAlias": "instance-2",
|
||||
"operationId": "getStats",
|
||||
"method": "GET",
|
||||
"pathTemplate": "/api/stats",
|
||||
"redacted": true,
|
||||
"statusCategory": "success",
|
||||
"httpStatus": 200,
|
||||
"contentType": "application/json",
|
||||
"latencyBucket": "1-3s",
|
||||
"payload": {
|
||||
"status": "ok",
|
||||
"message": "Success",
|
||||
"data": {
|
||||
"network_speed": {
|
||||
"interfaces": [
|
||||
{
|
||||
"interface": "wlan0",
|
||||
"rx_bytes_per_sec": 0,
|
||||
"tx_bytes_per_sec": 86,
|
||||
"total_rx_bytes": 0,
|
||||
"total_tx_bytes": 0
|
||||
}
|
||||
],
|
||||
"interval_seconds": 1
|
||||
},
|
||||
"memory": {
|
||||
"total_bytes": 0,
|
||||
"available_bytes": 0,
|
||||
"used_bytes": 0,
|
||||
"used_percent": 0,
|
||||
"cached_bytes": 0,
|
||||
"buffers_bytes": 37208064
|
||||
},
|
||||
"disk": [
|
||||
{
|
||||
"mount_point": "/",
|
||||
"fs_type": "ext4",
|
||||
"total_bytes": 0,
|
||||
"used_bytes": 0,
|
||||
"available_bytes": 0,
|
||||
"used_percent": 0
|
||||
},
|
||||
{
|
||||
"mount_point": "/run",
|
||||
"fs_type": "tmpfs",
|
||||
"total_bytes": 38850560,
|
||||
"used_bytes": 1277952,
|
||||
"available_bytes": 37572608,
|
||||
"used_percent": 0
|
||||
}
|
||||
],
|
||||
"cpu_load": {
|
||||
"load_1min": 0.08,
|
||||
"load_5min": 0.03,
|
||||
"load_15min": 0.01,
|
||||
"core_count": 4,
|
||||
"load_percent": 0
|
||||
},
|
||||
"uptime": {
|
||||
"uptime_seconds": 0,
|
||||
"idle_seconds": 0,
|
||||
"uptime_formatted": "[REDACTED]"
|
||||
},
|
||||
"system_info": {
|
||||
"sysname": "Linux",
|
||||
"nodename": "4G-wifi",
|
||||
"release": "[REDACTED]",
|
||||
"version": "[REDACTED]",
|
||||
"machine": "[REDACTED]",
|
||||
"full_info": "[REDACTED]"
|
||||
},
|
||||
"temperature": [
|
||||
{
|
||||
"zone": "[REDACTED]",
|
||||
"type": "[REDACTED]",
|
||||
"temperature": 0
|
||||
},
|
||||
{
|
||||
"zone": "[REDACTED]",
|
||||
"type": "[REDACTED]",
|
||||
"temperature": 0
|
||||
},
|
||||
{
|
||||
"zone": "[REDACTED]",
|
||||
"type": "[REDACTED]",
|
||||
"temperature": 0
|
||||
},
|
||||
{
|
||||
"zone": "[REDACTED]",
|
||||
"type": "[REDACTED]",
|
||||
"temperature": 0
|
||||
},
|
||||
{
|
||||
"zone": "[REDACTED]",
|
||||
"type": "[REDACTED]",
|
||||
"temperature": 0
|
||||
},
|
||||
{
|
||||
"zone": "[REDACTED]",
|
||||
"type": "[REDACTED]",
|
||||
"temperature": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"upstreamBaseline": "58e2204",
|
||||
"capturedAt": "2026-07-15",
|
||||
"sourceInstanceAlias": "instance-2",
|
||||
"operationId": "getStatsCpu",
|
||||
"method": "GET",
|
||||
"pathTemplate": "/api/stats/cpu",
|
||||
"redacted": true,
|
||||
"statusCategory": "success",
|
||||
"httpStatus": 200,
|
||||
"contentType": "application/json",
|
||||
"latencyBucket": "<250ms",
|
||||
"payload": {
|
||||
"status": "ok",
|
||||
"message": "Success",
|
||||
"data": {
|
||||
"core_count": 4,
|
||||
"cores": [
|
||||
{
|
||||
"processor": 0,
|
||||
"bogomips": "38.40",
|
||||
"features": [
|
||||
"fp",
|
||||
"asimd",
|
||||
"evtstrm",
|
||||
"crc32",
|
||||
"cpuid"
|
||||
],
|
||||
"implementer": "0x41",
|
||||
"architecture": "8",
|
||||
"variant": "0x0",
|
||||
"part": "0xd03",
|
||||
"revision": "[REDACTED]"
|
||||
},
|
||||
{
|
||||
"processor": 1,
|
||||
"bogomips": "38.40",
|
||||
"features": [
|
||||
"fp",
|
||||
"asimd",
|
||||
"evtstrm",
|
||||
"crc32",
|
||||
"cpuid"
|
||||
],
|
||||
"implementer": "0x41",
|
||||
"architecture": "8",
|
||||
"variant": "0x0",
|
||||
"part": "0xd03",
|
||||
"revision": "[REDACTED]"
|
||||
},
|
||||
{
|
||||
"processor": 2,
|
||||
"bogomips": "38.40",
|
||||
"features": [
|
||||
"fp",
|
||||
"asimd",
|
||||
"evtstrm",
|
||||
"crc32",
|
||||
"cpuid"
|
||||
],
|
||||
"implementer": "0x41",
|
||||
"architecture": "8",
|
||||
"variant": "0x0",
|
||||
"part": "0xd03",
|
||||
"revision": "[REDACTED]"
|
||||
},
|
||||
{
|
||||
"processor": 3,
|
||||
"bogomips": "38.40",
|
||||
"features": [
|
||||
"fp",
|
||||
"asimd",
|
||||
"evtstrm",
|
||||
"crc32",
|
||||
"cpuid"
|
||||
],
|
||||
"implementer": "0x41",
|
||||
"architecture": "8",
|
||||
"variant": "0x0",
|
||||
"part": "0xd03",
|
||||
"revision": "[REDACTED]"
|
||||
}
|
||||
],
|
||||
"hardware": "",
|
||||
"serial": "[REDACTED]",
|
||||
"model_name": "ARM CPU (part: 0xd03)"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"upstreamBaseline": "58e2204",
|
||||
"capturedAt": "2026-07-15",
|
||||
"sourceInstanceAlias": "instance-1",
|
||||
"operationId": "getAuthSettings",
|
||||
"method": "GET",
|
||||
"pathTemplate": "/api/auth/settings",
|
||||
"redacted": true,
|
||||
"statusCategory": "unsupported",
|
||||
"httpStatus": 404,
|
||||
"contentType": "text/plain",
|
||||
"latencyBucket": "<250ms",
|
||||
"payload": {
|
||||
"text": "[REDACTED]"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"upstreamBaseline": "58e2204",
|
||||
"capturedAt": "2026-07-15",
|
||||
"sourceInstanceAlias": "instance-1",
|
||||
"operationId": "getAuthStatus",
|
||||
"method": "GET",
|
||||
"pathTemplate": "/api/auth/status",
|
||||
"redacted": true,
|
||||
"statusCategory": "unsupported",
|
||||
"httpStatus": 404,
|
||||
"contentType": "text/plain",
|
||||
"latencyBucket": "<250ms",
|
||||
"payload": {
|
||||
"text": "[REDACTED]"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"upstreamBaseline": "58e2204",
|
||||
"capturedAt": "2026-07-15",
|
||||
"sourceInstanceAlias": "instance-1",
|
||||
"operationId": "getHealth",
|
||||
"method": "GET",
|
||||
"pathTemplate": "/api/health",
|
||||
"redacted": true,
|
||||
"statusCategory": "success",
|
||||
"httpStatus": 200,
|
||||
"contentType": "application/json",
|
||||
"latencyBucket": "<250ms",
|
||||
"payload": {
|
||||
"message": "Service is running",
|
||||
"platform": "linux-modem",
|
||||
"status": "ok",
|
||||
"version": "1.0.6"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"upstreamBaseline": "58e2204",
|
||||
"capturedAt": "2026-07-15",
|
||||
"sourceInstanceAlias": "instance-2",
|
||||
"operationId": "getAuthSettings",
|
||||
"method": "GET",
|
||||
"pathTemplate": "/api/auth/settings",
|
||||
"redacted": true,
|
||||
"statusCategory": "success",
|
||||
"httpStatus": 200,
|
||||
"contentType": "application/json",
|
||||
"latencyBucket": "<250ms",
|
||||
"payload": {
|
||||
"status": "ok",
|
||||
"message": "Success",
|
||||
"data": {
|
||||
"configured": true,
|
||||
"settings": {
|
||||
"password_protection_enabled": false,
|
||||
"password_min_length": 0,
|
||||
"password_require_letters": false,
|
||||
"password_require_digits": false,
|
||||
"password_require_symbols": false,
|
||||
"session_ttl_seconds": 604800,
|
||||
"idle_timeout_seconds": 3600
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"upstreamBaseline": "58e2204",
|
||||
"capturedAt": "2026-07-15",
|
||||
"sourceInstanceAlias": "instance-2",
|
||||
"operationId": "getAuthStatus",
|
||||
"method": "GET",
|
||||
"pathTemplate": "/api/auth/status",
|
||||
"redacted": true,
|
||||
"statusCategory": "success",
|
||||
"httpStatus": 200,
|
||||
"contentType": "application/json",
|
||||
"latencyBucket": "<250ms",
|
||||
"payload": {
|
||||
"status": "ok",
|
||||
"message": "Success",
|
||||
"data": {
|
||||
"configured": true,
|
||||
"authenticated": true,
|
||||
"settings": {
|
||||
"password_protection_enabled": false,
|
||||
"password_min_length": 0,
|
||||
"password_require_letters": false,
|
||||
"password_require_digits": false,
|
||||
"password_require_symbols": false,
|
||||
"session_ttl_seconds": 604800,
|
||||
"idle_timeout_seconds": 3600
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"upstreamBaseline": "58e2204",
|
||||
"capturedAt": "2026-07-15",
|
||||
"sourceInstanceAlias": "instance-2",
|
||||
"operationId": "getHealth",
|
||||
"method": "GET",
|
||||
"pathTemplate": "/api/health",
|
||||
"redacted": true,
|
||||
"statusCategory": "success",
|
||||
"httpStatus": 200,
|
||||
"contentType": "application/json",
|
||||
"latencyBucket": "<250ms",
|
||||
"payload": {
|
||||
"message": "Service is running",
|
||||
"platform": "linux-modem",
|
||||
"status": "ok",
|
||||
"version": "1.0.8"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"upstreamBaseline": "58e2204",
|
||||
"capturedAt": "2026-07-15",
|
||||
"sourceInstanceAlias": "instance-1",
|
||||
"operationId": "getSmsStats",
|
||||
"method": "GET",
|
||||
"pathTemplate": "/api/sms/stats",
|
||||
"redacted": true,
|
||||
"statusCategory": "success",
|
||||
"httpStatus": 200,
|
||||
"contentType": "application/json",
|
||||
"latencyBucket": "<250ms",
|
||||
"payload": {
|
||||
"status": "ok",
|
||||
"message": "Success",
|
||||
"data": {
|
||||
"total": 57,
|
||||
"incoming": 57,
|
||||
"outgoing": 0
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"upstreamBaseline": "58e2204",
|
||||
"capturedAt": "2026-07-15",
|
||||
"sourceInstanceAlias": "instance-2",
|
||||
"operationId": "getSmsStats",
|
||||
"method": "GET",
|
||||
"pathTemplate": "/api/sms/stats",
|
||||
"redacted": true,
|
||||
"statusCategory": "success",
|
||||
"httpStatus": 200,
|
||||
"contentType": "application/json",
|
||||
"latencyBucket": "<250ms",
|
||||
"payload": {
|
||||
"status": "ok",
|
||||
"message": "Success",
|
||||
"data": {
|
||||
"total": 11,
|
||||
"incoming": 11,
|
||||
"outgoing": 0,
|
||||
"pushed": 5,
|
||||
"push_attempted": 5
|
||||
}
|
||||
}
|
||||
}
|
||||
+166
@@ -0,0 +1,166 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"upstreamBaseline": "58e2204",
|
||||
"capturedAt": "2026-07-15",
|
||||
"sourceInstanceAlias": "instance-1",
|
||||
"operationId": "getNotificationsConfig",
|
||||
"method": "GET",
|
||||
"pathTemplate": "/api/notifications/config",
|
||||
"redacted": true,
|
||||
"statusCategory": "success",
|
||||
"httpStatus": 200,
|
||||
"contentType": "application/json",
|
||||
"latencyBucket": "<250ms",
|
||||
"payload": {
|
||||
"status": "ok",
|
||||
"message": "Success",
|
||||
"data": {
|
||||
"webhook": {
|
||||
"enabled": false,
|
||||
"url": "[REDACTED]",
|
||||
"forward_sms": false,
|
||||
"forward_calls": false,
|
||||
"forward_ddns": false,
|
||||
"forward_updates": false,
|
||||
"headers": {},
|
||||
"secret": "[REDACTED]",
|
||||
"sms_template": "[REDACTED]",
|
||||
"call_template": "[REDACTED]",
|
||||
"ddns_template": "[REDACTED]",
|
||||
"update_template": "[REDACTED]"
|
||||
},
|
||||
"bark": {
|
||||
"enabled": true,
|
||||
"forward_sms": true,
|
||||
"forward_calls": true,
|
||||
"forward_ddns": true,
|
||||
"forward_updates": true,
|
||||
"sms_template": "[REDACTED]",
|
||||
"call_template": "[REDACTED]",
|
||||
"ddns_template": "[REDACTED]",
|
||||
"update_template": "[REDACTED]",
|
||||
"server_url": "[REDACTED]",
|
||||
"device_key": "[REDACTED]",
|
||||
"title_template": "[REDACTED]",
|
||||
"group": "",
|
||||
"sound": "",
|
||||
"level": "",
|
||||
"icon": "",
|
||||
"click_url": "[REDACTED]",
|
||||
"copy": "",
|
||||
"auto_copy": false,
|
||||
"save_history": true
|
||||
},
|
||||
"pushplus": {
|
||||
"enabled": false,
|
||||
"forward_sms": true,
|
||||
"forward_calls": true,
|
||||
"forward_ddns": true,
|
||||
"forward_updates": true,
|
||||
"sms_template": "[REDACTED]",
|
||||
"call_template": "[REDACTED]",
|
||||
"ddns_template": "[REDACTED]",
|
||||
"update_template": "[REDACTED]",
|
||||
"token": "[REDACTED]",
|
||||
"title_template": "[REDACTED]",
|
||||
"topic": "",
|
||||
"template": "[REDACTED]",
|
||||
"channel": "",
|
||||
"option": "",
|
||||
"callback_url": "[REDACTED]"
|
||||
},
|
||||
"wecom_app": {
|
||||
"enabled": false,
|
||||
"forward_sms": true,
|
||||
"forward_calls": true,
|
||||
"forward_ddns": true,
|
||||
"forward_updates": true,
|
||||
"sms_template": "[REDACTED]",
|
||||
"call_template": "[REDACTED]",
|
||||
"ddns_template": "[REDACTED]",
|
||||
"update_template": "[REDACTED]",
|
||||
"corp_id": "",
|
||||
"agent_id": "",
|
||||
"secret": "[REDACTED]",
|
||||
"to_user": "[REDACTED]",
|
||||
"to_party": "",
|
||||
"to_tag": "",
|
||||
"safe": false
|
||||
},
|
||||
"wecom_robot": {
|
||||
"enabled": false,
|
||||
"forward_sms": true,
|
||||
"forward_calls": true,
|
||||
"forward_ddns": true,
|
||||
"forward_updates": true,
|
||||
"sms_template": "[REDACTED]",
|
||||
"call_template": "[REDACTED]",
|
||||
"ddns_template": "[REDACTED]",
|
||||
"update_template": "[REDACTED]",
|
||||
"webhook_url": "[REDACTED]",
|
||||
"key": ""
|
||||
},
|
||||
"dingtalk_robot": {
|
||||
"enabled": false,
|
||||
"forward_sms": true,
|
||||
"forward_calls": true,
|
||||
"forward_ddns": true,
|
||||
"forward_updates": true,
|
||||
"sms_template": "[REDACTED]",
|
||||
"call_template": "[REDACTED]",
|
||||
"ddns_template": "[REDACTED]",
|
||||
"update_template": "[REDACTED]",
|
||||
"webhook_url": "[REDACTED]",
|
||||
"access_token": "[REDACTED]",
|
||||
"secret": "[REDACTED]",
|
||||
"at_mobiles": "",
|
||||
"at_all": false
|
||||
},
|
||||
"dingtalk_app": {
|
||||
"enabled": false,
|
||||
"forward_sms": true,
|
||||
"forward_calls": true,
|
||||
"forward_ddns": true,
|
||||
"forward_updates": true,
|
||||
"sms_template": "[REDACTED]",
|
||||
"call_template": "[REDACTED]",
|
||||
"ddns_template": "[REDACTED]",
|
||||
"update_template": "[REDACTED]",
|
||||
"app_key": "",
|
||||
"app_secret": "[REDACTED]",
|
||||
"robot_code": "",
|
||||
"open_conversation_id": "",
|
||||
"msg_key": "sampleText"
|
||||
},
|
||||
"feishu_robot": {
|
||||
"enabled": false,
|
||||
"forward_sms": true,
|
||||
"forward_calls": true,
|
||||
"forward_ddns": true,
|
||||
"forward_updates": true,
|
||||
"sms_template": "[REDACTED]",
|
||||
"call_template": "[REDACTED]",
|
||||
"ddns_template": "[REDACTED]",
|
||||
"update_template": "[REDACTED]",
|
||||
"webhook_url": "[REDACTED]",
|
||||
"token": "[REDACTED]",
|
||||
"secret": "[REDACTED]"
|
||||
},
|
||||
"telegram": {
|
||||
"enabled": false,
|
||||
"forward_sms": true,
|
||||
"forward_calls": true,
|
||||
"forward_ddns": true,
|
||||
"forward_updates": true,
|
||||
"sms_template": "[REDACTED]",
|
||||
"call_template": "[REDACTED]",
|
||||
"ddns_template": "[REDACTED]",
|
||||
"update_template": "[REDACTED]",
|
||||
"bot_token": "[REDACTED]",
|
||||
"chat_id": "",
|
||||
"parse_mode": "",
|
||||
"disable_web_page_preview": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"upstreamBaseline": "58e2204",
|
||||
"capturedAt": "2026-07-15",
|
||||
"sourceInstanceAlias": "instance-2",
|
||||
"operationId": "getNotificationsConfig",
|
||||
"method": "GET",
|
||||
"pathTemplate": "/api/notifications/config",
|
||||
"redacted": true,
|
||||
"statusCategory": "success",
|
||||
"httpStatus": 200,
|
||||
"contentType": "application/json",
|
||||
"latencyBucket": "<250ms",
|
||||
"payload": {
|
||||
"status": "ok",
|
||||
"message": "Success",
|
||||
"data": {
|
||||
"version": 2,
|
||||
"channels": [
|
||||
{
|
||||
"id": "[REDACTED]",
|
||||
"type": "webhook",
|
||||
"name": "Webhook",
|
||||
"enabled": true,
|
||||
"config": {
|
||||
"headers": {},
|
||||
"secret": "[REDACTED]",
|
||||
"url": "[REDACTED]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "[REDACTED]",
|
||||
"type": "bark",
|
||||
"name": "Bark",
|
||||
"enabled": true,
|
||||
"config": {
|
||||
"auto_copy": false,
|
||||
"device_key": "[REDACTED]",
|
||||
"group": "[REDACTED]",
|
||||
"icon": "[REDACTED]",
|
||||
"level": "[REDACTED]",
|
||||
"save_history": false,
|
||||
"server_url": "[REDACTED]",
|
||||
"sound": "[REDACTED]",
|
||||
"title_template": "[REDACTED]"
|
||||
}
|
||||
}
|
||||
],
|
||||
"rules": [
|
||||
{
|
||||
"id": "[REDACTED]",
|
||||
"type": "sms",
|
||||
"name": "aut",
|
||||
"enabled": true,
|
||||
"matcher": {
|
||||
"field": "summary",
|
||||
"operator": "always",
|
||||
"value": ""
|
||||
},
|
||||
"channel_ids": [
|
||||
"[REDACTED]"
|
||||
],
|
||||
"event_codes": [],
|
||||
"template": "[REDACTED]",
|
||||
"quiet_hours": [],
|
||||
"ddns_failure_threshold": 1
|
||||
},
|
||||
{
|
||||
"id": "[REDACTED]",
|
||||
"type": "sms",
|
||||
"name": "默认短信规则",
|
||||
"enabled": true,
|
||||
"matcher": {
|
||||
"field": "summary",
|
||||
"operator": "always",
|
||||
"value": ""
|
||||
},
|
||||
"channel_ids": [
|
||||
"[REDACTED]"
|
||||
],
|
||||
"event_codes": [],
|
||||
"template": "[REDACTED]",
|
||||
"quiet_hours": [],
|
||||
"ddns_failure_threshold": 1
|
||||
}
|
||||
],
|
||||
"log_cleanup": {
|
||||
"retention_days_enabled": false,
|
||||
"retention_days": 90,
|
||||
"max_entries_enabled": false,
|
||||
"max_entries": 10000
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"upstreamBaseline": "58e2204",
|
||||
"capturedAt": "2026-07-15",
|
||||
"sourceInstanceAlias": "instance-1",
|
||||
"operationId": "getOtaStatus",
|
||||
"method": "GET",
|
||||
"pathTemplate": "/api/ota/status",
|
||||
"redacted": true,
|
||||
"statusCategory": "success",
|
||||
"httpStatus": 200,
|
||||
"contentType": "application/json",
|
||||
"latencyBucket": "<250ms",
|
||||
"payload": {
|
||||
"status": "ok",
|
||||
"message": "Success",
|
||||
"data": {
|
||||
"current_version": "1.0.6",
|
||||
"current_commit": "738330d",
|
||||
"pending_update": false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"upstreamBaseline": "58e2204",
|
||||
"capturedAt": "2026-07-15",
|
||||
"sourceInstanceAlias": "instance-2",
|
||||
"operationId": "getOtaStatus",
|
||||
"method": "GET",
|
||||
"pathTemplate": "/api/ota/status",
|
||||
"redacted": true,
|
||||
"statusCategory": "success",
|
||||
"httpStatus": 200,
|
||||
"contentType": "application/json",
|
||||
"latencyBucket": "<250ms",
|
||||
"payload": {
|
||||
"status": "ok",
|
||||
"message": "Success",
|
||||
"data": {
|
||||
"current_version": "1.0.8",
|
||||
"current_commit": "70da241",
|
||||
"pending_update": false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"upstreamBaseline": "58e2204",
|
||||
"capturedAt": "2026-07-15",
|
||||
"sourceInstanceAlias": "instance-1",
|
||||
"operationId": "getBandLock",
|
||||
"method": "GET",
|
||||
"pathTemplate": "/api/band-lock",
|
||||
"redacted": true,
|
||||
"statusCategory": "success",
|
||||
"httpStatus": 200,
|
||||
"contentType": "application/json",
|
||||
"latencyBucket": "<250ms",
|
||||
"payload": {
|
||||
"status": "ok",
|
||||
"message": "Success",
|
||||
"data": {
|
||||
"locked": true,
|
||||
"supported_lte_fdd_bands": [
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
5,
|
||||
7,
|
||||
8,
|
||||
20
|
||||
],
|
||||
"supported_lte_tdd_bands": [
|
||||
39,
|
||||
40
|
||||
],
|
||||
"supported_nr_fdd_bands": [],
|
||||
"supported_nr_tdd_bands": [],
|
||||
"lte_fdd_bands": [
|
||||
1,
|
||||
3,
|
||||
5
|
||||
],
|
||||
"lte_tdd_bands": [],
|
||||
"nr_fdd_bands": [],
|
||||
"nr_tdd_bands": []
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"upstreamBaseline": "58e2204",
|
||||
"capturedAt": "2026-07-15",
|
||||
"sourceInstanceAlias": "instance-1",
|
||||
"operationId": "getCellLock",
|
||||
"method": "GET",
|
||||
"pathTemplate": "/api/cell-lock",
|
||||
"redacted": true,
|
||||
"statusCategory": "success",
|
||||
"httpStatus": 200,
|
||||
"contentType": "application/json",
|
||||
"latencyBucket": "<250ms",
|
||||
"payload": {
|
||||
"status": "ok",
|
||||
"message": "Success",
|
||||
"data": {
|
||||
"rat_status": [
|
||||
{
|
||||
"rat": 12,
|
||||
"rat_name": "LTE",
|
||||
"enabled": false,
|
||||
"lock_type": 0,
|
||||
"pci": null,
|
||||
"arfcn": null
|
||||
},
|
||||
{
|
||||
"rat": 16,
|
||||
"rat_name": "NR",
|
||||
"enabled": false,
|
||||
"lock_type": 0,
|
||||
"pci": null,
|
||||
"arfcn": null
|
||||
}
|
||||
],
|
||||
"any_locked": false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"upstreamBaseline": "58e2204",
|
||||
"capturedAt": "2026-07-15",
|
||||
"sourceInstanceAlias": "instance-1",
|
||||
"operationId": "getRadioMode",
|
||||
"method": "GET",
|
||||
"pathTemplate": "/api/radio-mode",
|
||||
"redacted": true,
|
||||
"statusCategory": "success",
|
||||
"httpStatus": 200,
|
||||
"contentType": "application/json",
|
||||
"latencyBucket": "<250ms",
|
||||
"payload": {
|
||||
"status": "ok",
|
||||
"message": "Success",
|
||||
"data": {
|
||||
"mode": "lte",
|
||||
"technology_preference": "lte-advanced",
|
||||
"supported_modes": [
|
||||
"auto",
|
||||
"lte"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"upstreamBaseline": "58e2204",
|
||||
"capturedAt": "2026-07-15",
|
||||
"sourceInstanceAlias": "instance-2",
|
||||
"operationId": "getBandLock",
|
||||
"method": "GET",
|
||||
"pathTemplate": "/api/band-lock",
|
||||
"redacted": true,
|
||||
"statusCategory": "success",
|
||||
"httpStatus": 200,
|
||||
"contentType": "application/json",
|
||||
"latencyBucket": "<250ms",
|
||||
"payload": {
|
||||
"status": "ok",
|
||||
"message": "Success",
|
||||
"data": {
|
||||
"locked": true,
|
||||
"supported_lte_fdd_bands": [
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
5,
|
||||
7,
|
||||
8,
|
||||
20
|
||||
],
|
||||
"supported_lte_tdd_bands": [
|
||||
39,
|
||||
40
|
||||
],
|
||||
"supported_nr_fdd_bands": [],
|
||||
"supported_nr_tdd_bands": [],
|
||||
"lte_fdd_bands": [
|
||||
1,
|
||||
3,
|
||||
5
|
||||
],
|
||||
"lte_tdd_bands": [],
|
||||
"nr_fdd_bands": [],
|
||||
"nr_tdd_bands": []
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"upstreamBaseline": "58e2204",
|
||||
"capturedAt": "2026-07-15",
|
||||
"sourceInstanceAlias": "instance-2",
|
||||
"operationId": "getCellLock",
|
||||
"method": "GET",
|
||||
"pathTemplate": "/api/cell-lock",
|
||||
"redacted": true,
|
||||
"statusCategory": "success",
|
||||
"httpStatus": 200,
|
||||
"contentType": "application/json",
|
||||
"latencyBucket": "<250ms",
|
||||
"payload": {
|
||||
"status": "ok",
|
||||
"message": "Success",
|
||||
"data": {
|
||||
"rat_status": [
|
||||
{
|
||||
"rat": 12,
|
||||
"rat_name": "LTE",
|
||||
"enabled": false,
|
||||
"lock_type": 0,
|
||||
"pci": null,
|
||||
"arfcn": null
|
||||
},
|
||||
{
|
||||
"rat": 16,
|
||||
"rat_name": "NR",
|
||||
"enabled": false,
|
||||
"lock_type": 0,
|
||||
"pci": null,
|
||||
"arfcn": null
|
||||
}
|
||||
],
|
||||
"any_locked": false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"upstreamBaseline": "58e2204",
|
||||
"capturedAt": "2026-07-15",
|
||||
"sourceInstanceAlias": "instance-2",
|
||||
"operationId": "getRadioMode",
|
||||
"method": "GET",
|
||||
"pathTemplate": "/api/radio-mode",
|
||||
"redacted": true,
|
||||
"statusCategory": "success",
|
||||
"httpStatus": 200,
|
||||
"contentType": "application/json",
|
||||
"latencyBucket": "<250ms",
|
||||
"payload": {
|
||||
"status": "ok",
|
||||
"message": "Success",
|
||||
"data": {
|
||||
"mode": "lte",
|
||||
"technology_preference": "lte-advanced",
|
||||
"supported_modes": [
|
||||
"auto",
|
||||
"lte"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"upstreamBaseline": "58e2204",
|
||||
"capturedAt": "2026-07-15",
|
||||
"sourceInstanceAlias": "instance-1",
|
||||
"operationId": "getSim",
|
||||
"method": "GET",
|
||||
"pathTemplate": "/api/sim",
|
||||
"redacted": true,
|
||||
"statusCategory": "success",
|
||||
"httpStatus": 200,
|
||||
"contentType": "application/json",
|
||||
"latencyBucket": "250ms-1s",
|
||||
"payload": {
|
||||
"status": "ok",
|
||||
"message": "Success",
|
||||
"data": {
|
||||
"present": true,
|
||||
"iccid": "[REDACTED]",
|
||||
"imsi": "[REDACTED]",
|
||||
"phone_numbers": [
|
||||
"[REDACTED]"
|
||||
],
|
||||
"sms_center": "[REDACTED]",
|
||||
"mcc": "460",
|
||||
"mnc": "02",
|
||||
"phone_number_is_manual": false,
|
||||
"sms_center_is_manual": false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"upstreamBaseline": "58e2204",
|
||||
"capturedAt": "2026-07-15",
|
||||
"sourceInstanceAlias": "instance-2",
|
||||
"operationId": "getSim",
|
||||
"method": "GET",
|
||||
"pathTemplate": "/api/sim",
|
||||
"redacted": true,
|
||||
"statusCategory": "success",
|
||||
"httpStatus": 200,
|
||||
"contentType": "application/json",
|
||||
"latencyBucket": "<250ms",
|
||||
"payload": {
|
||||
"status": "ok",
|
||||
"message": "Success",
|
||||
"data": {
|
||||
"present": true,
|
||||
"iccid": "[REDACTED]",
|
||||
"imsi": "[REDACTED]",
|
||||
"phone_numbers": [
|
||||
"[REDACTED]"
|
||||
],
|
||||
"sms_center": "[REDACTED]",
|
||||
"mcc": "460",
|
||||
"mnc": "00",
|
||||
"phone_number_is_manual": true,
|
||||
"sms_center_is_manual": false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"upstreamBaseline": "58e2204",
|
||||
"capturedAt": "2026-07-15",
|
||||
"sourceInstanceAlias": "instance-1",
|
||||
"operationId": "getEsimConfig",
|
||||
"method": "GET",
|
||||
"pathTemplate": "/api/esim/config",
|
||||
"redacted": true,
|
||||
"statusCategory": "unsupported",
|
||||
"httpStatus": 404,
|
||||
"contentType": "text/plain",
|
||||
"latencyBucket": "<250ms",
|
||||
"payload": {
|
||||
"text": "[REDACTED]"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"upstreamBaseline": "58e2204",
|
||||
"capturedAt": "2026-07-15",
|
||||
"sourceInstanceAlias": "instance-1",
|
||||
"operationId": "getEsimLpacStatus",
|
||||
"method": "GET",
|
||||
"pathTemplate": "/api/esim/lpac/status",
|
||||
"redacted": true,
|
||||
"statusCategory": "auth-required",
|
||||
"httpStatus": 403,
|
||||
"contentType": "application/json",
|
||||
"latencyBucket": "<250ms",
|
||||
"payload": {
|
||||
"status": "error",
|
||||
"message": "eSIM module is disabled in current work mode"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"upstreamBaseline": "58e2204",
|
||||
"capturedAt": "2026-07-15",
|
||||
"sourceInstanceAlias": "instance-1",
|
||||
"operationId": "getWorkMode",
|
||||
"method": "GET",
|
||||
"pathTemplate": "/api/work-mode",
|
||||
"redacted": true,
|
||||
"statusCategory": "success",
|
||||
"httpStatus": 200,
|
||||
"contentType": "application/json",
|
||||
"latencyBucket": "<250ms",
|
||||
"payload": {
|
||||
"status": "ok",
|
||||
"message": "Success",
|
||||
"data": {
|
||||
"mode": "sim",
|
||||
"worker_running": false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"upstreamBaseline": "58e2204",
|
||||
"capturedAt": "2026-07-15",
|
||||
"sourceInstanceAlias": "instance-2",
|
||||
"operationId": "getEsimConfig",
|
||||
"method": "GET",
|
||||
"pathTemplate": "/api/esim/config",
|
||||
"redacted": true,
|
||||
"statusCategory": "unsupported",
|
||||
"httpStatus": 404,
|
||||
"contentType": "text/plain",
|
||||
"latencyBucket": "<250ms",
|
||||
"payload": {
|
||||
"text": "[REDACTED]"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"upstreamBaseline": "58e2204",
|
||||
"capturedAt": "2026-07-15",
|
||||
"sourceInstanceAlias": "instance-2",
|
||||
"operationId": "getEsimLpacStatus",
|
||||
"method": "GET",
|
||||
"pathTemplate": "/api/esim/lpac/status",
|
||||
"redacted": true,
|
||||
"statusCategory": "auth-required",
|
||||
"httpStatus": 403,
|
||||
"contentType": "application/json",
|
||||
"latencyBucket": "<250ms",
|
||||
"payload": {
|
||||
"status": "error",
|
||||
"message": "eSIM module is disabled in current work mode"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"upstreamBaseline": "58e2204",
|
||||
"capturedAt": "2026-07-15",
|
||||
"sourceInstanceAlias": "instance-2",
|
||||
"operationId": "getWorkMode",
|
||||
"method": "GET",
|
||||
"pathTemplate": "/api/work-mode",
|
||||
"redacted": true,
|
||||
"statusCategory": "success",
|
||||
"httpStatus": 200,
|
||||
"contentType": "application/json",
|
||||
"latencyBucket": "<250ms",
|
||||
"payload": {
|
||||
"status": "ok",
|
||||
"message": "Success",
|
||||
"data": {
|
||||
"mode": "sim",
|
||||
"worker_running": false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
import { parseResponseSensitivePath } from './redactor.ts';
|
||||
|
||||
export type Shape={type:string,keys?:Record<string,Shape>,items?:Shape[]};
|
||||
|
||||
export function payloadShape(value:any):Shape {
|
||||
if(value===null)return {type:'null'};
|
||||
if(Array.isArray(value))return {type:'array',items:value.map(payloadShape)};
|
||||
if(typeof value==='object')return {type:'object',keys:Object.fromEntries(Object.keys(value).sort().map(k=>[k,payloadShape(value[k])]))};
|
||||
return {type:typeof value};
|
||||
}
|
||||
|
||||
export function validateShapeNode(node:any):void {
|
||||
if(!node||typeof node!=='object'||Array.isArray(node))throw new Error('invalid shape node');
|
||||
const allowed=node.type==='object'?['keys','type']:node.type==='array'?['items','type']:['type'];
|
||||
if(!['object','array','string','number','boolean','null'].includes(node.type)||JSON.stringify(Object.keys(node).sort())!==JSON.stringify(allowed))throw new Error('invalid shape node');
|
||||
if(node.type==='object'){
|
||||
if(!node.keys||typeof node.keys!=='object'||Array.isArray(node.keys))throw new Error('invalid shape keys');
|
||||
for(const child of Object.values(node.keys))validateShapeNode(child);
|
||||
} else if(node.type==='array'){
|
||||
if(!Array.isArray(node.items))throw new Error('invalid shape items');
|
||||
for(const child of node.items)validateShapeNode(child);
|
||||
}
|
||||
}
|
||||
|
||||
function canonicalPrimitive(value:any){
|
||||
return value===null||value==='[REDACTED]'||(typeof value==='number'&&value===0)||(typeof value==='boolean'&&value===false);
|
||||
}
|
||||
function assertCanonicalTree(value:any):void {
|
||||
if(Array.isArray(value)){for(const item of value)assertCanonicalTree(item);return;}
|
||||
if(value&&typeof value==='object'){for(const item of Object.values(value))assertCanonicalTree(item);return;}
|
||||
if(!canonicalPrimitive(value))throw new Error('non-canonical sensitive leaf');
|
||||
}
|
||||
function matchedNodes(node:any,parts:string[],i=0):any[]{
|
||||
if(i===parts.length)return [node];
|
||||
if(parts[i]==='*')return Array.isArray(node)?node.flatMap(x=>matchedNodes(x,parts,i+1)):[];
|
||||
return node&&typeof node==='object'&&parts[i] in node?matchedNodes(node[parts[i]],parts,i+1):[];
|
||||
}
|
||||
// Deliberately excludes ordinary `message`; only credential-like generic keys belong here.
|
||||
const GENERIC_SENSITIVE_KEY=/(?:password|passwd|credential|api[_-]?key|access[_-]?(?:key|token|secret|id)|device[_-]?key|private[_-]?key|(?:^|[_-])pin(?:$|[_-])|puk|token|secret|cookie|authorization|confirmation[_-]?code)/i;
|
||||
export function validateSensitivePayload(payload:any,operation:any):void {
|
||||
for(const field of operation?.sensitiveFields||[]){
|
||||
if(field?.direction!=='response')continue;
|
||||
for(const node of matchedNodes(payload,parseResponseSensitivePath(field.path)))assertCanonicalTree(node);
|
||||
}
|
||||
function walk(node:any):void{
|
||||
if(Array.isArray(node)){for(const x of node)walk(x);return;}
|
||||
if(node&&typeof node==='object')for(const [key,value] of Object.entries(node)){if(GENERIC_SENSITIVE_KEY.test(key))assertCanonicalTree(value);else walk(value);}
|
||||
}
|
||||
walk(payload);
|
||||
}
|
||||
|
||||
const isJson=(s:any)=>typeof s==='string'&&(s==='application/json'||s.endsWith('+json'));
|
||||
const exactPayload=(actual:any,expected:any)=>JSON.stringify(actual)===JSON.stringify(expected);
|
||||
export function validateStatusContract(f:any):void {
|
||||
const status=f.httpStatus,category=f.statusCategory;
|
||||
if(category==='success'){
|
||||
if(!(Number.isInteger(status)&&status>=200&&status<300&&isJson(f.contentType)))throw new Error('inconsistent success status');
|
||||
}else if(category==='auth-required'){
|
||||
if(![401,403].includes(status))throw new Error('inconsistent auth status');
|
||||
}else if(category==='unsupported'){
|
||||
if(![404,405,501].includes(status))throw new Error('inconsistent unsupported status');
|
||||
}else if(category==='non-json'){
|
||||
const valid=Number.isInteger(status)&&status>=200&&status<300&&((!isJson(f.contentType)&&exactPayload(f.payload,{text:'[REDACTED]'}))||(isJson(f.contentType)&&exactPayload(f.payload,{error:'invalid-json'})));
|
||||
if(!valid)throw new Error('inconsistent non-json status');
|
||||
}else if(category==='http-error'){
|
||||
// Redirects are observed with redirect:'manual' and are non-success HTTP outcomes.
|
||||
if(!(Number.isInteger(status)&&status>=300&&status<600&&![401,403,404,405,501].includes(status)))throw new Error('inconsistent http error status');
|
||||
}else if(category==='timeout'){
|
||||
if(status!==null||f.contentType!==null||f.latencyBucket!=='timeout'||!exactPayload(f.payload,{error:'timeout'}))throw new Error('inconsistent timeout status');
|
||||
}else if(category==='network-error'){
|
||||
if(status!==null||f.contentType!==null||f.latencyBucket==='timeout'||!exactPayload(f.payload,{error:'network-error'}))throw new Error('inconsistent network status');
|
||||
}else throw new Error('unknown status category');
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { readFile, readdir, stat } from 'node:fs/promises';
|
||||
import { createHash } from 'node:crypto';
|
||||
import path from 'node:path';
|
||||
import { upstream58e2204Operations } from '../../operation-registry/src/upstream-58e2204.ts';
|
||||
import { collectOne, selectReadonlyOperations, validateReadonlyOperation, DENY_REASONS } from '../scripts/collector.ts';
|
||||
import { redact } from '../src/redactor.ts';
|
||||
import { payloadShape, validateShapeNode, validateSensitivePayload, validateStatusContract } from '../src/verifier.ts';
|
||||
|
||||
const root = path.resolve(import.meta.dirname, '..');
|
||||
const fixtureRoot = path.join(root, 'src/simadmin');
|
||||
const allowedEnvelope = ['schemaVersion','upstreamBaseline','capturedAt','sourceInstanceAlias','operationId','method','pathTemplate','statusCategory','httpStatus','contentType','latencyBucket','redacted','payload'];
|
||||
// Preserve business-schema keys such as `url` and channel `headers`; sensitive values
|
||||
// are typed-redacted. The exact top-level envelope already excludes transport metadata.
|
||||
const forbiddenKeys = /^(raw(response)?|instance(id|name))$/i;
|
||||
const DENIED_PATHS = new Set(Object.keys(DENY_REASONS));
|
||||
const leakPatterns = [
|
||||
/https?:\/\//i, /\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/i,
|
||||
/\b(?:\d{1,3}\.){3}\d{1,3}\b/, /(?:[0-9a-f]{2}:){5}[0-9a-f]{2}/i,
|
||||
/\+?\d(?:[ ()-]*\d){9,14}/, /\b\d{14,22}\b/,
|
||||
/\b(?:bearer\s+)?[A-Za-z0-9_-]{32,}\b/i,
|
||||
];
|
||||
|
||||
async function jsonFiles(dir:string):Promise<string[]> { const out:string[]=[]; for(const e of await readdir(dir,{withFileTypes:true})){const p=path.join(dir,e.name); if(e.isDirectory()) out.push(...await jsonFiles(p)); else if(e.name.endsWith('.json')) out.push(p);} return out; }
|
||||
function walkKeys(v:any, cb:(k:string)=>void){ if(Array.isArray(v)) return v.forEach(x=>walkKeys(x,cb)); if(v&&typeof v==='object') for(const [k,x] of Object.entries(v)){cb(k);walkKeys(x,cb);} }
|
||||
function stringLeaves(v:any,out:string[]=[]){if(Array.isArray(v))v.forEach(x=>stringLeaves(x,out));else if(v&&typeof v==='object')Object.values(v).forEach(x=>stringLeaves(x,out));else if(typeof v==='string'&&v.length>=3)out.push(v);return out;}
|
||||
|
||||
test('selector is registry-derived passive GET R0 only with explicit denials', () => {
|
||||
const { selected, denied } = selectReadonlyOperations(upstream58e2204Operations);
|
||||
assert.ok(selected.length > 20);
|
||||
assert.ok(denied.some((x:any)=>x.pathTemplate==='/api/network/operators/scan' && x.denyReason));
|
||||
for(const op of selected){ assert.equal(op.method,'GET'); assert.equal(op.riskLevel,'R0'); assert.doesNotMatch(op.pathTemplate,/[{}]/); assert.notEqual(op.pathTemplate,'/api/network/operators/scan'); }
|
||||
});
|
||||
|
||||
test('collector source has no write-method or authentication escape hatch', async () => {
|
||||
const source = await readFile(path.join(root,'scripts/collect-readonly-fixtures.ts'),'utf8');
|
||||
assert.doesNotMatch(source, /ensureAuthenticated|login\s*\(|--method|--path|--origin/i);
|
||||
assert.doesNotMatch(source, /['"](?:POST|PUT|PATCH|DELETE)['"]/);
|
||||
});
|
||||
|
||||
test('transport can only receive credential-free GET, manual redirect and timeout', async () => {
|
||||
let init:any; const fake=async (_url:any, i:any)=>{init=i; return new Response(JSON.stringify({phone:'+15551234567',message:'private',ip:'10.1.2.3'}),{status:200,headers:{'content-type':'application/json','set-cookie':'bad=1'}})};
|
||||
const op=upstream58e2204Operations.find(x=>x.operationId==='getHealth')!;
|
||||
const f=await collectOne({origin:'http://192.168.1.2',alias:'instance-1'},op,fake as any);
|
||||
assert.deepEqual({method:init.method,body:init.body,redirect:init.redirect,credentials:init.credentials},{method:'GET',body:undefined,redirect:'manual',credentials:'omit'});
|
||||
assert.deepEqual(init.headers, { accept: 'application/json' }); assert.ok(init.signal);
|
||||
assert.equal(f.payload.phone,'[REDACTED]'); assert.equal(f.payload.message,'private'); assert.equal(f.payload.ip,'[REDACTED]');
|
||||
});
|
||||
|
||||
test('redactor covers sensitive keys and value patterns deterministically',()=>{
|
||||
const x=redact({password:'p',TOKEN:'t',content:'body',phone:'x',other:['+155****4567','+155****4567','a@b.example','https://private.example/x','aa:bb:cc:dd:ee:ff','2001:db8::1']});
|
||||
assert.equal(x.password,'[REDACTED]'); assert.equal(x.TOKEN,'[REDACTED]'); assert.equal(x.content,'[REDACTED]'); assert.equal(x.phone,'[REDACTED]');
|
||||
for(const p of leakPatterns) assert.doesNotMatch(JSON.stringify(x),p);
|
||||
});
|
||||
|
||||
test('Registry operation identity also rejects in-place structural mutation',()=>{
|
||||
const op:any=upstream58e2204Operations.find(x=>x.operationId==='getHealth')!;
|
||||
const original=structuredClone(op);
|
||||
const mutations=[
|
||||
(x:any)=>x.sensitiveFields.push({direction:'response',path:'$.response.data.secret'}),
|
||||
(x:any)=>{delete x.pathTemplate;},
|
||||
(x:any)=>{x.method='POST';},
|
||||
(x:any)=>{x.sensitiveFields='changed';},
|
||||
];
|
||||
for(const mutate of mutations){
|
||||
try{mutate(op);assert.throws(()=>validateReadonlyOperation(op),/Registry integrity/);}
|
||||
finally{for(const key of Object.keys(op))delete op[key];Object.assign(op,structuredClone(original));}
|
||||
}
|
||||
validateReadonlyOperation(op);
|
||||
});
|
||||
|
||||
test('collector categorizes status and JSON parse outcomes consistently',async()=>{
|
||||
const op=upstream58e2204Operations.find(x=>x.operationId==='getHealth')!;
|
||||
const run=(status:number,type:string,body:string|null)=>collectOne({origin:'http://192.168.1.2',alias:'instance-1'},op,async()=>new Response(body,{status,headers:{'content-type':type}}));
|
||||
for(const [status,type,body,category] of [[200,'application/problem+json','{}','success'],[200,'application/json','bad','non-json'],[204,'text/plain',null,'non-json'],[401,'application/json','{}','auth-required'],[405,'application/json','{}','unsupported'],[500,'application/json','{}','http-error']] as const){
|
||||
const f=await run(status,type,body);assert.equal(f.statusCategory,category);validateStatusContract(f);
|
||||
}
|
||||
const redirect=await run(302,'application/json','{}');assert.equal(redirect.statusCategory,'http-error');validateStatusContract(redirect);
|
||||
assert.throws(()=>validateStatusContract({statusCategory:'success',httpStatus:401,contentType:'application/json',payload:{},latencyBucket:'<250ms'}));
|
||||
});
|
||||
|
||||
test('real fixtures satisfy envelope, registry, leak scan, and manifest integrity', async()=>{
|
||||
const manifest=JSON.parse(await readFile(path.join(root,'src/manifest.json'),'utf8'));assert.deepEqual(Object.keys(manifest).sort(),['domainCoverage','files','realFixtureCount','schemaVersion','shapeBaseline','syntheticFixtureCount','upstreamBaseline']); const files=(await jsonFiles(fixtureRoot)).filter(x=>!x.includes('/synthetic-errors/'));
|
||||
const srcEntries=await readdir(path.join(root,'src'));assert.ok(!srcEntries.includes('synthetic-errors'));
|
||||
const config=JSON.parse(await readFile(path.resolve(root,'../../config.json'),'utf8'));
|
||||
const privateValues=stringLeaves(config);
|
||||
const expected=selectReadonlyOperations(upstream58e2204Operations).selected;
|
||||
assert.equal(files.length,expected.length*2); assert.equal(manifest.realFixtureCount,files.length);assert.equal(manifest.syntheticFixtureCount,0);
|
||||
assert.equal(manifest.schemaVersion,1);assert.equal(manifest.upstreamBaseline,'58e2204');
|
||||
const shapePath=path.join(root,'src/response-shapes-58e2204.json');const shapeText=await readFile(shapePath,'utf8');const shapeBaseline=JSON.parse(shapeText);
|
||||
assert.deepEqual(Object.keys(shapeBaseline).sort(),['schemaVersion','shapes','upstreamBaseline']);assert.equal(shapeBaseline.schemaVersion,1);assert.equal(shapeBaseline.upstreamBaseline,'58e2204');assert.equal(Object.keys(shapeBaseline.shapes).length,78);
|
||||
assert.deepEqual(Object.keys(manifest.shapeBaseline).sort(),['path','sha256','size']);assert.equal(manifest.shapeBaseline.path,'src/response-shapes-58e2204.json');assert.equal(manifest.shapeBaseline.size,(await stat(shapePath)).size);assert.equal(manifest.shapeBaseline.sha256,createHash('sha256').update(shapeText).digest('hex'));assert.ok(!manifest.files.some((x:any)=>x.path===manifest.shapeBaseline.path));
|
||||
for(const node of Object.values(shapeBaseline.shapes))validateShapeNode(node);
|
||||
const registry=new Map(upstream58e2204Operations.map(x=>[x.operationId,x])); const domains=new Map<string,number>();const pairs=new Set<string>();
|
||||
const disk=files.map(file=>path.relative(root,file)).sort();const listed=manifest.files.map((x:any)=>x.path).sort();assert.deepEqual(listed,disk);assert.equal(new Set(listed).size,listed.length);
|
||||
for(const file of files){const text=await readFile(file,'utf8'); const f=JSON.parse(text); assert.deepEqual(Object.keys(f).sort(),[...allowedEnvelope].sort()); assert.equal(f.schemaVersion,1);assert.equal(f.upstreamBaseline,'58e2204');assert.match(f.capturedAt,/^20(?:2[4-9]|[3-9]\d)-\d{2}-\d{2}$/);assert.ok(!Number.isNaN(Date.parse(`${f.capturedAt}T00:00:00Z`)));assert.ok(['instance-1','instance-2'].includes(f.sourceInstanceAlias));assert.equal(f.redacted,true); assert.equal(f.method,'GET'); assert.ok(!('synthetic' in f));assert.ok(['success','non-json','auth-required','unsupported','http-error','timeout','network-error'].includes(f.statusCategory));if(['timeout','network-error'].includes(f.statusCategory))assert.equal(f.httpStatus,null);else assert.ok(Number.isInteger(f.httpStatus)); const op:any=registry.get(f.operationId);assert.ok(op);validateStatusContract(f);validateSensitivePayload(f.payload,op);const shapeKey=`${f.sourceInstanceAlias}::${f.operationId}`;assert.deepEqual(payloadShape(f.payload),shapeBaseline.shapes[shapeKey]); assert.equal(op?.riskLevel,'R0');assert.equal(op?.method,'GET'); assert.equal(op?.pathTemplate,f.pathTemplate);assert.ok(!DENIED_PATHS.has(f.pathTemplate));domains.set(op.upstreamDomain,(domains.get(op.upstreamDomain)||0)+1);const pair=`${f.sourceInstanceAlias}:${f.operationId}`;assert.ok(!pairs.has(pair));pairs.add(pair); walkKeys(f,k=>assert.doesNotMatch(k,forbiddenKeys)); for(const p of leakPatterns) assert.doesNotMatch(text,p); for(const value of privateValues) assert.ok(!text.includes(value),'fixture contains configured value'); const rel=path.relative(root,file); const m=manifest.files.find((x:any)=>x.path===rel); assert.ok(m); assert.equal(m.size,(await stat(file)).size);assert.match(m.sha256,/^[a-f0-9]{64}$/); assert.equal(m.sha256,createHash('sha256').update(text).digest('hex')); }
|
||||
assert.deepEqual(Object.fromEntries([...domains].sort()),manifest.domainCoverage);
|
||||
assert.deepEqual(Object.keys(shapeBaseline.shapes).sort(),[...pairs].map(x=>x.replace(':','::')).sort());
|
||||
assert.deepEqual([...pairs].sort(),expected.flatMap((op:any)=>['instance-1','instance-2'].map(alias=>`${alias}:${op.operationId}`)).sort());
|
||||
});
|
||||
@@ -0,0 +1,90 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { upstream58e2204Operations } from '../../operation-registry/src/upstream-58e2204.ts';
|
||||
import { collectOne, selectReadonlyOperations, validateReadonlyOperation, validateInstanceOrigin } from '../scripts/collector.ts';
|
||||
import { loadInstances } from '../scripts/collect-readonly-fixtures.ts';
|
||||
import { redact, redactOperationPayload, parseResponseSensitivePath } from '../src/redactor.ts';
|
||||
|
||||
const health=upstream58e2204Operations.find((x:any)=>x.operationId==='getHealth')!;
|
||||
const fakeResponse=async()=>new Response('{"ok":true}',{headers:{'content-type':'application/json'}});
|
||||
|
||||
test('operation validator rejects path and registry identity bypasses', async()=>{
|
||||
const attacks=['https://example.invalid/api/health','//example.invalid/api/health','/api/health?scan=1','/api/health#x','/api/%73can','/api/%252e%252e/health','/api/a/../health','/api\\health','/api//health'];
|
||||
for(const pathTemplate of attacks) assert.throws(()=>validateReadonlyOperation({...health,pathTemplate}));
|
||||
assert.throws(()=>validateReadonlyOperation({...health}));
|
||||
assert.throws(()=>validateReadonlyOperation({operationId:health.operationId,method:'GET',riskLevel:'R0',pathTemplate:health.pathTemplate}));
|
||||
await assert.rejects(()=>collectOne({origin:'http://192.168.1.2',alias:'instance-1'},{...health},fakeResponse));
|
||||
});
|
||||
|
||||
test('all path bypass classes fail before transport',async()=>{
|
||||
let calls=0;const transport=async()=>{calls++;return new Response('{}',{headers:{'content-type':'application/json'}})};
|
||||
const attacks=['https://evil.invalid/api/health','//evil.invalid/api/health','/api/health?q=1','/api/health#x','/api/%68ealth','/api/%252e%252e/x','/api/a/../x','/api\\health','/api//health'];
|
||||
for(const pathTemplate of attacks) await assert.rejects(()=>collectOne({origin:'http://192.168.1.2',alias:'instance-1'},{...health,pathTemplate},transport));
|
||||
assert.equal(calls,0);
|
||||
assert.throws(()=>selectReadonlyOperations([{...health}]));
|
||||
});
|
||||
|
||||
test('collectOne validates RFC1918 origin before transport',()=>{
|
||||
for(const origin of ['http://127.0.0.1','http://169.254.1.1','http://100.100.100.200','http://8.8.8.8','http://router.local','http://u:p@192.168.1.2','http://192.168.1.2/x','http://192.168.1.2?q=1','ftp://192.168.1.2']) assert.throws(()=>validateInstanceOrigin(origin));
|
||||
assert.equal(validateInstanceOrigin('https://172.16.2.3:8443'),'https://172.16.2.3:8443');
|
||||
});
|
||||
|
||||
test('passive selection explicitly denies connectivity and active scan',()=>{
|
||||
const {selected,denied}=selectReadonlyOperations(upstream58e2204Operations);
|
||||
assert.ok(denied.some((x:any)=>x.pathTemplate==='/api/connectivity'));
|
||||
assert.ok(denied.some((x:any)=>x.pathTemplate==='/api/network/operators/scan'));
|
||||
assert.ok(!selected.some((x:any)=>x.pathTemplate==='/api/connectivity'));
|
||||
});
|
||||
|
||||
test('config uses formal validation plus collector LAN/auth/origin constraints',()=>{
|
||||
const good={instances:[{id:'a',url:'http://192.168.1.2',auth:{mode:'none'}},{id:'b',url:'https://10.0.0.2',auth:{mode:'none'}}]};
|
||||
assert.deepEqual(loadInstances(good).map((x:any)=>x.alias),['instance-1','instance-2']);
|
||||
const bad=['http://127.0.0.1','http://169.254.1.2','http://100.100.100.200','http://8.8.8.8','http://router.local','http://u:***@192.168.1.2','http://192.168.1.2/path','http://192.168.1.2?q=1','http://192.168.1.2#x','http://[::1]'];
|
||||
for(const url of bad) assert.throws(()=>loadInstances({instances:[{id:'a',url},{id:'b',url:'http://10.0.0.2'}]}));
|
||||
assert.throws(()=>loadInstances({instances:[{id:'a',url:'http://192.168.1.2',auth:{password:'x'}},{id:'b',url:'http://10.0.0.2'}]}));
|
||||
assert.throws(()=>loadInstances({instances:[{id:'a',url:'http://192.168.1.2'}]}));
|
||||
assert.throws(()=>loadInstances({instances:[{id:'a',url:'http://192.168.1.2'},{id:'b',url:'http://10.0.0.2'},{id:'c',url:'http://10.0.0.3'}]}));
|
||||
});
|
||||
|
||||
test('directional response JSONPaths redact every declared path and preserve shape/types',()=>{
|
||||
const ops=upstream58e2204Operations.filter((op:any)=>op.sensitiveFields.some((f:any)=>f.direction==='response'));
|
||||
assert.ok(ops.length>0);
|
||||
for(const op of ops) for(const field of op.sensitiveFields.filter((f:any)=>f.direction==='response')){
|
||||
const parts=parseResponseSensitivePath(field.path); assert.ok(parts.length>0,`${op.operationId} unsupported response path`);
|
||||
let leaf:any={s:'private',n:42,b:true,z:null,a:['x',{q:9}]};
|
||||
let payload:any=leaf;
|
||||
for(let i=parts.length-1;i>=0;i--){const p=parts[i]; payload=p==='*'?[payload]:{[p]:payload};}
|
||||
const out:any=redactOperationPayload(payload,op);
|
||||
let hit=out; for(const p of parts) hit=p==='*'?hit[0]:hit[p];
|
||||
assert.deepEqual(hit,{s:'[REDACTED]',n:0,b:false,z:null,a:['[REDACTED]',{q:0}]});
|
||||
}
|
||||
});
|
||||
|
||||
test('deep sanitizer handles reviewed key/value bypasses without schema destruction',()=>{
|
||||
const input:any={credential:'x',api_key:'x',access_key:'x',device_key:'x',private_key:'x',pin:12,puk:34,username:'x',user:'x',account:'x',serial:'x',revision:'x',path:'/private/a',template:'user=x token=abc123 path=/private/a',config:{access_token:'tiny'},phone_number_is_manual:true,sms_center_is_manual:false,message:'API is healthy',nested:'{"username":"x","enabled":true}',host:'private.internal:8080',short_number:'12345678',split_id:'12-34-56-78-90-12-34',ipv6:'::1%lo0',latitude:1.234567,lon:2.345678,cell_id:99,lac:2,tac:3,pci:4,enb:5,gnb:6,uptime:777,traffic:888,timestamp:'2026-07-16T12:34:56Z',unknown:'AKIAIOSFODNN7EXAMPLE'};
|
||||
const x:any=redact(input);
|
||||
for(const k of ['credential','api_key','access_key','device_key','private_key','username','user','account','serial','revision','path','template','host','short_number','split_id','ipv6','timestamp','unknown']) assert.equal(x[k],'[REDACTED]');
|
||||
for(const k of ['pin','puk','latitude','lon','cell_id','lac','tac','pci','enb','gnb','uptime','traffic']) assert.equal(x[k],0);
|
||||
assert.equal(typeof x.nested,'string'); assert.deepEqual(JSON.parse(x.nested),{username:'[REDACTED]',enabled:true});
|
||||
assert.deepEqual(x.config,{access_token:'[REDACTED]'}); assert.equal(x.phone_number_is_manual,true); assert.equal(x.sms_center_is_manual,false); assert.equal(x.message,'API is healthy');
|
||||
});
|
||||
|
||||
test('schema shapes and primitive types survive conservative redaction',()=>{
|
||||
const x:any=redact({phone_numbers:['12345678','87654321'],ip_addresses:[{v4:'192.168.1.9',active:true}],urls:['private.example/x'],webhook:{url:'//private.example/h',enabled:true},config:{s:'secret',n:42,b:true,z:null,a:['x',{n:9}]}});
|
||||
assert.ok(Array.isArray(x.phone_numbers));assert.equal(x.phone_numbers.length,2);
|
||||
assert.ok(Array.isArray(x.ip_addresses));assert.equal(typeof x.ip_addresses[0],'object');
|
||||
assert.ok(Array.isArray(x.urls));assert.equal(typeof x.webhook,'object');
|
||||
assert.deepEqual(x.config,{s:'[REDACTED]',n:0,b:false,z:null,a:['[REDACTED]',{n:0}]});
|
||||
});
|
||||
|
||||
test('invalid declared JSON and non-JSON bodies are non-json and discarded',async()=>{
|
||||
const invalid=await collectOne({origin:'http://192.168.1.2',alias:'instance-1'},health,async()=>new Response('{bad',{status:200,headers:{'content-type':'application/json'}}));
|
||||
assert.equal(invalid.statusCategory,'non-json');assert.deepEqual(invalid.payload,{error:'invalid-json'});
|
||||
const text=await collectOne({origin:'http://192.168.1.2',alias:'instance-1'},health,async()=>new Response('private body',{status:200,headers:{'content-type':'text/plain'}}));
|
||||
assert.equal(text.statusCategory,'non-json');assert.deepEqual(text.payload,{text:'[REDACTED]'});
|
||||
});
|
||||
|
||||
test('collector errors never echo rejected origin values',async()=>{
|
||||
const secret='http://user:password@evil.invalid/private?token=x';
|
||||
try{await collectOne({origin:secret,alias:'instance-1'},health,fakeResponse);assert.fail('expected rejection');}catch(error:any){assert.ok(!String(error.message).includes(secret));assert.ok(!String(error.message).includes('password'));}
|
||||
});
|
||||
Reference in New Issue
Block a user