This tool is experimental. It helps tune CAD thresholds and symbol settings using live channel observations, but results can vary with traffic and RF conditions.
Use when no intentional signal is present. Goal: minimize false CAD detections and instability (timeouts/errors).
Use while generating compatible traffic on the same freq/SF/BW/CR. Goal: confirm reliable CAD detections with stable behavior.
This tool is experimental. It helps tune CAD thresholds and symbol settings using live channel observations, but results can vary with traffic and RF conditions.
Use when no intentional signal is present. Goal: minimize false CAD detections and instability (timeouts/errors).
Use while generating compatible traffic on the same freq/SF/BW/CR. Goal: confirm reliable CAD detections with stable behavior.
0&&e.stroke()}}function on(e,t,n){return n||=.5,!t||e&&e.x>t.left-n&&e.x c(i,y,_)&&s(i,y)!==0,x=()=>s(a,_)===0||c(a,y,_),S=()=>h||b(),C=()=>!h||x();for(let e=u,n=u;e<=d;++e)v=t[e%o],!v.skip&&(_=l(v[r]),_!==y&&(h=c(_,i,a),g===null&&S()&&(g=s(_,i)===0?e:n),g!==null&&C()&&(m.push(Er({start:g,end:e,loop:f,count:o,style:p})),g=null),n=e,y=_));return g!==null&&m.push(Er({start:g,end:d,loop:f,count:o,style:p})),m}function kr(e,t){let n=[],r=e.segments;for(let i=0;i 0&&e.stroke()}}function on(e,t,n){return n||=.5,!t||e&&e.x>t.left-n&&e.x c(i,y,_)&&s(i,y)!==0,x=()=>s(a,_)===0||c(a,y,_),S=()=>h||b(),C=()=>!h||x();for(let e=u,n=u;e<=d;++e)v=t[e%o],!v.skip&&(_=l(v[r]),_!==y&&(h=c(_,i,a),g===null&&S()&&(g=s(_,i)===0?e:n),g!==null&&C()&&(m.push(Er({start:g,end:e,loop:f,count:o,style:p})),g=null),n=e,y=_));return g!==null&&m.push(Er({start:g,end:d,loop:f,count:o,style:p})),m}function kr(e,t){let n=[],r=e.segments;for(let i=0;i Manage companion identities (TCP frame server) Manage companion identities (TCP frame server) API tokens are used for machine-to-machine authentication. Include the token in the Tokens are only shown once at creation. Store them securely. openHop Console must be installed at There are three layers of advert rate limit control: Each layer can be enabled/disabled independently and the others will still function. Decision flow when all enabled: Adaptive tier check → Penalty box check → Token bucket check → Violation recording (triggers penalty box) Activity tiers:Quiet (bypass limiting) → Normal (lighter: 0.5x intervals) → Busy (base: 1.0x intervals) → Congested (stricter: 2.0x intervals) Note: Adaptive mode scales refill/min-interval timing; bucket capacity stays at the configured base value. Mesh traffic can reach your repeater through different paths, so duplicate advert packets are expected. This is normal behavior and helps prevent repeated rebroadcasts from flooding the mesh. Each sender has a token bucket. Every forwarded advert uses one token. If a sender keeps hitting the limit, it is temporarily blocked. Adaptive mode adjusts limits based on recent advert activity. This page is served over HTTP, not HTTPS. Exported data (including identity keys) will be transmitted in plain text. Only use these features on a trusted local network. Download a complete backup including all passwords, JWT secrets, and identity keys. Required for restoring to a new device or recovering from a failed SD card. Contains sensitive data. The backup file will include plain-text passwords and private keys. Store it securely and never share it. Download the repeater's private identity key for backup. This key determines the node's address and cryptographic identity on the mesh. Sensitive data. The identity key is the repeater's private key. Anyone with this key can impersonate your node. Store the exported file securely and never share it. Memory tracing is running. Let the repeater run for a few minutes, then click Check Again to see which parts of the code are using more memory. API tokens are used for machine-to-machine authentication. Include the token in the Tokens are only shown once at creation. Store them securely. openHop Console must be installed at There are three layers of advert rate limit control: Each layer can be enabled/disabled independently and the others will still function. Decision flow when all enabled: Adaptive tier check → Penalty box check → Token bucket check → Violation recording (triggers penalty box) Activity tiers:Quiet (bypass limiting) → Normal (lighter: 0.5x intervals) → Busy (base: 1.0x intervals) → Congested (stricter: 2.0x intervals) Note: Adaptive mode scales refill/min-interval timing; bucket capacity stays at the configured base value. Mesh traffic can reach your repeater through different paths, so duplicate advert packets are expected. This is normal behavior and helps prevent repeated rebroadcasts from flooding the mesh. Each sender has a token bucket. Every forwarded advert uses one token. If a sender keeps hitting the limit, it is temporarily blocked. Adaptive mode adjusts limits based on recent advert activity. This page is served over HTTP, not HTTPS. Exported data (including identity keys) will be transmitted in plain text. Only use these features on a trusted local network. Download a complete backup including all passwords, JWT secrets, and identity keys. Required for restoring to a new device or recovering from a failed SD card. Contains sensitive data. The backup file will include plain-text passwords and private keys. Store it securely and never share it. Download the repeater's private identity key for backup. This key determines the node's address and cryptographic identity on the mesh. Sensitive data. The identity key is the repeater's private key. Anyone with this key can impersonate your node. Store the exported file securely and never share it. Memory tracing is running. Let the repeater run for a few minutes, then click Check Again to see which parts of the code are using more memory. Activity (Last 24 Hours) Activity (Last 24 Hours) {let n=this.getDatasetMeta(e);if(!n)throw Error(`No dataset found at index `+e);return{datasetIndex:e,element:n.data[t],index:t}});Ne(n,t)||(this._active=n,this._lastEvent=null,this._updateHoverStyles(n,t))}notifyPlugins(e,t,n){return this._plugins.notify(this,e,t,n)}isPluginEnabled(e){return this._plugins._cache.filter(t=>t.plugin.id===e).length===1}_updateHoverStyles(e,t,n){let r=this.options.hover,i=(e,t)=>e.filter(e=>!t.some(t=>e.datasetIndex===t.datasetIndex&&e.index===t.index)),a=i(t,e),o=n?e:i(e,t);a.length&&this.updateHoverStyle(a,r.mode,!1),o.length&&r.mode&&this.updateHoverStyle(o,r.mode,!0)}_eventHandler(e,t){let n={event:e,replay:t,cancelable:!0,inChartArea:this.isPointInArea(e)},r=t=>(t.options.events||this.options.events).includes(e.native.type);if(this.notifyPlugins(`beforeEvent`,n,r)===!1)return;let i=this._handleEvent(e,t,n.inChartArea);return n.cancelable=!1,this.notifyPlugins(`afterEvent`,n,r),(i||n.changed)&&this.render(),this}_handleEvent(e,t,n){let{_active:r=[],options:i}=this,a=t,o=this._getActiveElements(e,r,n,a),s=Je(e),c=Io(e,this._lastEvent,n,s);n&&(this._lastEvent=null,F(i.onHover,[e,o,this],this),s&&F(i.onClick,[e,o,this],this));let l=!Ne(o,r);return(l||t)&&(this._active=o,this._updateHoverStyles(o,r,t)),this._lastEvent=c,l}_getActiveElements(e,t,n,r){if(e.type===`mouseout`)return[];if(!n)return t;let i=this.options.hover;return this.getElementsAtEventForMode(e,i.mode,i,r)}};function Ro(){return I(Lo.instances,e=>e._plugins.invalidate())}function zo(e,t,n){let{startAngle:r,x:i,y:a,outerRadius:o,innerRadius:s,options:c}=t,{borderWidth:l,borderJoinStyle:u}=c,d=Math.min(l/o,H(r-n));if(e.beginPath(),e.arc(i,a,o-l/2,r+d/2,n-d/2),s>0){let t=Math.min(l/s,H(r-n));e.arc(i,a,s+l/2,n-t/2,r+t/2,!0)}else{let t=Math.min(l/2,o*H(r-n));if(u===`round`)e.arc(i,a,t,n-L/2,r+L/2,!0);else if(u===`bevel`){let o=2*t*t,s=-o*Math.cos(n+L/2)+i,c=-o*Math.sin(n+L/2)+a,l=o*Math.cos(r+L/2)+i,u=o*Math.sin(r+L/2)+a;e.lineTo(s,c),e.lineTo(l,u)}}e.closePath(),e.moveTo(0,0),e.rect(0,0,e.canvas.width,e.canvas.height),e.clip(`evenodd`)}function Bo(e,t,n){let{startAngle:r,pixelMargin:i,x:a,y:o,outerRadius:s,innerRadius:c}=t,l=i/s;e.beginPath(),e.arc(a,o,s,r-l,n+l),c>i?(l=i/c,e.arc(a,o,c,n+l,r-l,!0)):e.arc(a,o,i,n+z,r-z),e.closePath(),e.clip()}function Vo(e){return bn(e,[`outerStart`,`outerEnd`,`innerStart`,`innerEnd`])}function Ho(e,t,n,r){let i=Vo(e.options.borderRadius),a=(n-t)/2,o=Math.min(a,r*t/2),s=e=>{let t=(n-Math.min(a,e))*r/2;return U(e,0,Math.min(a,t))};return{outerStart:s(i.outerStart),outerEnd:s(i.outerEnd),innerStart:U(i.innerStart,0,o),innerEnd:U(i.innerEnd,0,o)}}function Uo(e,t,n,r){return{x:n+e*Math.cos(t),y:r+e*Math.sin(t)}}function Wo(e,t,n,r,i,a){let{x:o,y:s,startAngle:c,pixelMargin:l,innerRadius:u}=t,d=Math.max(t.outerRadius+r+n-l,0),f=u>0?u+r+n+l:0,p=0,m=i-c;if(r){let e=((u>0?u-r:0)+(d>0?d-r:0))/2;p=(m-(e===0?m:m*e/(e+r)))/2}let h=(m-Math.max(.001,m*d-n/L)/d)/2,g=c+h+p,_=i-h-p,{outerStart:v,outerEnd:y,innerStart:b,innerEnd:x}=Ho(t,f,d,_-g),S=d-v,C=d-y,w=g+v/S,T=_-y/C,E=f+b,D=f+x,ee=g+b/E,O=_-x/D;if(e.beginPath(),a){let t=(w+T)/2;if(e.arc(o,s,d,w,t),e.arc(o,s,d,t,T),y>0){let t=Uo(C,T,o,s);e.arc(t.x,t.y,y,T,_+z)}let n=Uo(D,_,o,s);if(e.lineTo(n.x,n.y),x>0){let t=Uo(D,O,o,s);e.arc(t.x,t.y,x,_+z,O+Math.PI)}let r=(_-x/f+(g+b/f))/2;if(e.arc(o,s,f,_-x/f,r,!0),e.arc(o,s,f,r,g+b/f,!0),b>0){let t=Uo(E,ee,o,s);e.arc(t.x,t.y,b,ee+Math.PI,g-z)}let i=Uo(S,g,o,s);if(e.lineTo(i.x,i.y),v>0){let t=Uo(S,w,o,s);e.arc(t.x,t.y,v,g-z,w)}}else{e.moveTo(o,s);let t=Math.cos(w)*d+o,n=Math.sin(w)*d+s;e.lineTo(t,n);let r=Math.cos(T)*d+o,i=Math.sin(T)*d+s;e.lineTo(r,i)}e.closePath()}function Go(e,t,n,r,i){let{fullCircles:a,startAngle:o,circumference:s}=t,c=t.endAngle;if(a){Wo(e,t,n,r,c,i);for(let t=0;t=L&&p===0&&u!==`miter`&&zo(e,t,h),a||(Wo(e,t,n,r,h,i),e.stroke())}var qo=class extends ka{static id=`arc`;static defaults={borderAlign:`center`,borderColor:`#fff`,borderDash:[],borderDashOffset:0,borderJoinStyle:void 0,borderRadius:0,borderWidth:2,offset:0,spacing:0,angle:void 0,circular:!0,selfJoin:!1};static defaultRoutes={backgroundColor:`backgroundColor`};static descriptors={_scriptable:!0,_indexable:e=>e!==`borderDash`};circumference;endAngle;fullCircles;innerRadius;outerRadius;pixelMargin;startAngle;constructor(e){super(),this.options=void 0,this.circumference=void 0,this.startAngle=void 0,this.endAngle=void 0,this.innerRadius=void 0,this.outerRadius=void 0,this.pixelMargin=0,this.fullCircles=0,e&&Object.assign(this,e)}inRange(e,t,n){let{angle:r,distance:i}=ut(this.getProps([`x`,`y`],n),{x:e,y:t}),{startAngle:a,endAngle:o,innerRadius:s,outerRadius:c,circumference:l}=this.getProps([`startAngle`,`endAngle`,`innerRadius`,`outerRadius`,`circumference`],n),u=(this.options.spacing+this.options.borderWidth)/2,d=P(l,o-a),f=pt(r,a,o)&&a!==o,p=d>=R||f,m=W(i,s+u,c+u);return p&&m}getCenterPoint(e){let{x:t,y:n,startAngle:r,endAngle:i,innerRadius:a,outerRadius:o}=this.getProps([`x`,`y`,`startAngle`,`endAngle`,`innerRadius`,`outerRadius`],e),{offset:s,spacing:c}=this.options,l=(r+i)/2,u=(a+o+c+s)/2;return{x:t+Math.cos(l)*u,y:n+Math.sin(l)*u}}tooltipPosition(e){return this.getCenterPoint(e)}draw(e){let{options:t,circumference:n}=this,r=(t.offset||0)/4,i=(t.spacing||0)/2,a=t.circular;if(this.pixelMargin=t.borderAlign===`inner`?.33:0,this.fullCircles=n>R?Math.floor(n/R):0,n===0||this.innerRadius<0||this.outerRadius<0)return;e.save();let o=(this.startAngle+this.endAngle)/2;e.translate(Math.cos(o)*r,Math.sin(o)*r);let s=r*(1-Math.sin(Math.min(L,n||0)));e.fillStyle=t.backgroundColor,e.strokeStyle=t.borderColor,Go(e,this,s,i,a),Ko(e,this,s,i,a),e.restore()}};function Jo(e,t,n=t){e.lineCap=P(n.borderCapStyle,t.borderCapStyle),e.setLineDash(P(n.borderDash,t.borderDash)),e.lineDashOffset=P(n.borderDashOffset,t.borderDashOffset),e.lineJoin=P(n.borderJoinStyle,t.borderJoinStyle),e.lineWidth=P(n.borderWidth,t.borderWidth),e.strokeStyle=P(n.borderColor,t.borderColor)}function Yo(e,t,n){e.lineTo(n.x,n.y)}function Xo(e){return e.stepped?ln:e.tension||e.cubicInterpolationMode===`monotone`?un:Yo}function Zo(e,t,n={}){let r=e.length,{start:i=0,end:a=r-1}=n,{start:o,end:s}=t,c=Math.max(i,o),l=Math.min(a,s),u=in.length){for(c=0;c{let n=this.getDatasetMeta(e);if(!n)throw Error(`No dataset found at index `+e);return{datasetIndex:e,element:n.data[t],index:t}});Ne(n,t)||(this._active=n,this._lastEvent=null,this._updateHoverStyles(n,t))}notifyPlugins(e,t,n){return this._plugins.notify(this,e,t,n)}isPluginEnabled(e){return this._plugins._cache.filter(t=>t.plugin.id===e).length===1}_updateHoverStyles(e,t,n){let r=this.options.hover,i=(e,t)=>e.filter(e=>!t.some(t=>e.datasetIndex===t.datasetIndex&&e.index===t.index)),a=i(t,e),o=n?e:i(e,t);a.length&&this.updateHoverStyle(a,r.mode,!1),o.length&&r.mode&&this.updateHoverStyle(o,r.mode,!0)}_eventHandler(e,t){let n={event:e,replay:t,cancelable:!0,inChartArea:this.isPointInArea(e)},r=t=>(t.options.events||this.options.events).includes(e.native.type);if(this.notifyPlugins(`beforeEvent`,n,r)===!1)return;let i=this._handleEvent(e,t,n.inChartArea);return n.cancelable=!1,this.notifyPlugins(`afterEvent`,n,r),(i||n.changed)&&this.render(),this}_handleEvent(e,t,n){let{_active:r=[],options:i}=this,a=t,o=this._getActiveElements(e,r,n,a),s=Je(e),c=Io(e,this._lastEvent,n,s);n&&(this._lastEvent=null,F(i.onHover,[e,o,this],this),s&&F(i.onClick,[e,o,this],this));let l=!Ne(o,r);return(l||t)&&(this._active=o,this._updateHoverStyles(o,r,t)),this._lastEvent=c,l}_getActiveElements(e,t,n,r){if(e.type===`mouseout`)return[];if(!n)return t;let i=this.options.hover;return this.getElementsAtEventForMode(e,i.mode,i,r)}};function Ro(){return I(Lo.instances,e=>e._plugins.invalidate())}function zo(e,t,n){let{startAngle:r,x:i,y:a,outerRadius:o,innerRadius:s,options:c}=t,{borderWidth:l,borderJoinStyle:u}=c,d=Math.min(l/o,H(r-n));if(e.beginPath(),e.arc(i,a,o-l/2,r+d/2,n-d/2),s>0){let t=Math.min(l/s,H(r-n));e.arc(i,a,s+l/2,n-t/2,r+t/2,!0)}else{let t=Math.min(l/2,o*H(r-n));if(u===`round`)e.arc(i,a,t,n-L/2,r+L/2,!0);else if(u===`bevel`){let o=2*t*t,s=-o*Math.cos(n+L/2)+i,c=-o*Math.sin(n+L/2)+a,l=o*Math.cos(r+L/2)+i,u=o*Math.sin(r+L/2)+a;e.lineTo(s,c),e.lineTo(l,u)}}e.closePath(),e.moveTo(0,0),e.rect(0,0,e.canvas.width,e.canvas.height),e.clip(`evenodd`)}function Bo(e,t,n){let{startAngle:r,pixelMargin:i,x:a,y:o,outerRadius:s,innerRadius:c}=t,l=i/s;e.beginPath(),e.arc(a,o,s,r-l,n+l),c>i?(l=i/c,e.arc(a,o,c,n+l,r-l,!0)):e.arc(a,o,i,n+z,r-z),e.closePath(),e.clip()}function Vo(e){return bn(e,[`outerStart`,`outerEnd`,`innerStart`,`innerEnd`])}function Ho(e,t,n,r){let i=Vo(e.options.borderRadius),a=(n-t)/2,o=Math.min(a,r*t/2),s=e=>{let t=(n-Math.min(a,e))*r/2;return U(e,0,Math.min(a,t))};return{outerStart:s(i.outerStart),outerEnd:s(i.outerEnd),innerStart:U(i.innerStart,0,o),innerEnd:U(i.innerEnd,0,o)}}function Uo(e,t,n,r){return{x:n+e*Math.cos(t),y:r+e*Math.sin(t)}}function Wo(e,t,n,r,i,a){let{x:o,y:s,startAngle:c,pixelMargin:l,innerRadius:u}=t,d=Math.max(t.outerRadius+r+n-l,0),f=u>0?u+r+n+l:0,p=0,m=i-c;if(r){let e=((u>0?u-r:0)+(d>0?d-r:0))/2;p=(m-(e===0?m:m*e/(e+r)))/2}let h=(m-Math.max(.001,m*d-n/L)/d)/2,g=c+h+p,_=i-h-p,{outerStart:v,outerEnd:y,innerStart:b,innerEnd:x}=Ho(t,f,d,_-g),S=d-v,C=d-y,w=g+v/S,T=_-y/C,E=f+b,D=f+x,ee=g+b/E,O=_-x/D;if(e.beginPath(),a){let t=(w+T)/2;if(e.arc(o,s,d,w,t),e.arc(o,s,d,t,T),y>0){let t=Uo(C,T,o,s);e.arc(t.x,t.y,y,T,_+z)}let n=Uo(D,_,o,s);if(e.lineTo(n.x,n.y),x>0){let t=Uo(D,O,o,s);e.arc(t.x,t.y,x,_+z,O+Math.PI)}let r=(_-x/f+(g+b/f))/2;if(e.arc(o,s,f,_-x/f,r,!0),e.arc(o,s,f,r,g+b/f,!0),b>0){let t=Uo(E,ee,o,s);e.arc(t.x,t.y,b,ee+Math.PI,g-z)}let i=Uo(S,g,o,s);if(e.lineTo(i.x,i.y),v>0){let t=Uo(S,w,o,s);e.arc(t.x,t.y,v,g-z,w)}}else{e.moveTo(o,s);let t=Math.cos(w)*d+o,n=Math.sin(w)*d+s;e.lineTo(t,n);let r=Math.cos(T)*d+o,i=Math.sin(T)*d+s;e.lineTo(r,i)}e.closePath()}function Go(e,t,n,r,i){let{fullCircles:a,startAngle:o,circumference:s}=t,c=t.endAngle;if(a){Wo(e,t,n,r,c,i);for(let t=0;t=L&&p===0&&u!==`miter`&&zo(e,t,h),a||(Wo(e,t,n,r,h,i),e.stroke())}var qo=class extends ka{static id=`arc`;static defaults={borderAlign:`center`,borderColor:`#fff`,borderDash:[],borderDashOffset:0,borderJoinStyle:void 0,borderRadius:0,borderWidth:2,offset:0,spacing:0,angle:void 0,circular:!0,selfJoin:!1};static defaultRoutes={backgroundColor:`backgroundColor`};static descriptors={_scriptable:!0,_indexable:e=>e!==`borderDash`};circumference;endAngle;fullCircles;innerRadius;outerRadius;pixelMargin;startAngle;constructor(e){super(),this.options=void 0,this.circumference=void 0,this.startAngle=void 0,this.endAngle=void 0,this.innerRadius=void 0,this.outerRadius=void 0,this.pixelMargin=0,this.fullCircles=0,e&&Object.assign(this,e)}inRange(e,t,n){let{angle:r,distance:i}=ut(this.getProps([`x`,`y`],n),{x:e,y:t}),{startAngle:a,endAngle:o,innerRadius:s,outerRadius:c,circumference:l}=this.getProps([`startAngle`,`endAngle`,`innerRadius`,`outerRadius`,`circumference`],n),u=(this.options.spacing+this.options.borderWidth)/2,d=P(l,o-a),f=pt(r,a,o)&&a!==o,p=d>=R||f,m=W(i,s+u,c+u);return p&&m}getCenterPoint(e){let{x:t,y:n,startAngle:r,endAngle:i,innerRadius:a,outerRadius:o}=this.getProps([`x`,`y`,`startAngle`,`endAngle`,`innerRadius`,`outerRadius`],e),{offset:s,spacing:c}=this.options,l=(r+i)/2,u=(a+o+c+s)/2;return{x:t+Math.cos(l)*u,y:n+Math.sin(l)*u}}tooltipPosition(e){return this.getCenterPoint(e)}draw(e){let{options:t,circumference:n}=this,r=(t.offset||0)/4,i=(t.spacing||0)/2,a=t.circular;if(this.pixelMargin=t.borderAlign===`inner`?.33:0,this.fullCircles=n>R?Math.floor(n/R):0,n===0||this.innerRadius<0||this.outerRadius<0)return;e.save();let o=(this.startAngle+this.endAngle)/2;e.translate(Math.cos(o)*r,Math.sin(o)*r);let s=r*(1-Math.sin(Math.min(L,n||0)));e.fillStyle=t.backgroundColor,e.strokeStyle=t.borderColor,Go(e,this,s,i,a),Ko(e,this,s,i,a),e.restore()}};function Jo(e,t,n=t){e.lineCap=P(n.borderCapStyle,t.borderCapStyle),e.setLineDash(P(n.borderDash,t.borderDash)),e.lineDashOffset=P(n.borderDashOffset,t.borderDashOffset),e.lineJoin=P(n.borderJoinStyle,t.borderJoinStyle),e.lineWidth=P(n.borderWidth,t.borderWidth),e.strokeStyle=P(n.borderColor,t.borderColor)}function Yo(e,t,n){e.lineTo(n.x,n.y)}function Xo(e){return e.stepped?ln:e.tension||e.cubicInterpolationMode===`monotone`?un:Yo}function Zo(e,t,n={}){let r=e.length,{start:i=0,end:a=r-1}=n,{start:o,end:s}=t,c=Math.max(i,o),l=Math.min(a,s),u=i Companions
Companions
X-API-Key header when making API requests. /opt/pymc_console/web/html before selecting this option. Why you may see the same advert more than once
Token Bucket Rate Limiting
- Copy 1 forwarded (2 → 1 tokens)
- Copy 2 forwarded (1 → 0 tokens)
- Copy 3 dropped (no tokens left) Penalty Box (Repeat Offenders)
Adaptive Mesh Activity Tiers
- 0.02 adverts/min → QUIET (bypass)
- 0.35 adverts/min → BUSY (tighter limits)
- 0.68 adverts/min → CONGESTED (strict limits) Recommended starting settings
Unencrypted Connection
Full Backup
Export Identity Key
X-API-Key header when making API requests. /opt/pymc_console/web/html before selecting this option. Why you may see the same advert more than once
Token Bucket Rate Limiting
- Copy 1 forwarded (2 → 1 tokens)
- Copy 2 forwarded (1 → 0 tokens)
- Copy 3 dropped (no tokens left) Penalty Box (Repeat Offenders)
Adaptive Mesh Activity Tiers
- 0.02 adverts/min → QUIET (bypass)
- 0.35 adverts/min → BUSY (tighter limits)
- 0.68 adverts/min → CONGESTED (strict limits) Recommended starting settings
Unencrypted Connection
Full Backup
Export Identity Key
[e.rxUtil,e.txUtil]))*1.05;k.value=Math.max(5,Math.ceil(g/5)*5),Y={data:h,yAxisMax:k.value,fetchedAt:Date.now()},C.value=!1,w.value=!1,T.value=!1,D.value=null,S(()=>F())}catch(e){console.error(`Failed to fetch airtime data:`,e),v.value=[],C.value=!1,w.value=!1,T.value=!1,D.value=e instanceof Error?e.message:`Failed to load chart data`,S(()=>F())}},F=()=>{if(!p.value)return;let e=p.value,t=e.getContext(`2d`);if(!t)return;let n=e.parentElement;if(!n)return;let r=n.getBoundingClientRect(),i=r.width,a=r.height;if(e.width=i*window.devicePixelRatio,e.height=a*window.devicePixelRatio,e.style.width=i+`px`,e.style.height=a+`px`,t.scale(window.devicePixelRatio,window.devicePixelRatio),t.clearRect(0,0,i,a),C.value){t.fillStyle=u().axisLabel,t.font=`16px system-ui`,t.textAlign=`center`,t.fillText(`Loading chart data...`,i/2,a/2);return}if(v.value.length===0){t.fillStyle=u().axisLabel,t.font=`16px system-ui`,t.textAlign=`center`,t.fillText(`No data available`,i/2,a/2);return}let o=i-45-20,s=a-40,l=k.value,d=k.value,f=u();t.strokeStyle=f.gridLine,t.lineWidth=1,t.font=`10px system-ui`,t.textAlign=`right`;for(let e=0;e<=5;e++){let n=20+s*e/5;t.beginPath(),t.moveTo(45,n),t.lineTo(i-20,n),t.stroke();let r=l-e/5*d;t.fillStyle=f.axisLabel,t.fillText(`${r.toFixed(0)}%`,40,n+3)}for(let e=0;e<=6;e++){let n=45+o*e/6;t.beginPath(),t.moveTo(n,20),t.lineTo(n,a-20),t.stroke()}v.value.length>1&&(t.strokeStyle=c.rx,t.lineWidth=2,t.beginPath(),v.value.forEach((e,n)=>{let r=45+o*n/(v.value.length-1),i=a-20-Math.min(e.rxUtil,k.value)/d*s;n===0?t.moveTo(r,i):t.lineTo(r,i)}),t.stroke()),v.value.length>1&&(t.strokeStyle=c.tx,t.lineWidth=2,t.beginPath(),v.value.forEach((e,n)=>{let r=45+o*n/(v.value.length-1),i=a-20-Math.min(e.txUtil,k.value)/d*s;n===0?t.moveTo(r,i):t.lineTo(r,i)}),t.stroke())};return M(P,{intervalMs:12e4,immediate:!1}),e(()=>{Y&&Date.now()-Y.fetchedAt Airtime Utilization
je(r),[`stop`])},[n[18]||=y(`svg`,{class:`w-5 h-5`,fill:`none`,stroke:`currentColor`,viewBox:`0 0 24 24`,"aria-hidden":`true`},[y(`rect`,{x:`9`,y:`9`,width:`11`,height:`11`,rx:`2`,ry:`2`,"stroke-width":`2`}),y(`path`,{d:`M6 15H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v1`,"stroke-width":`2`,"stroke-linecap":`round`,"stroke-linejoin":`round`})],-1),y(`span`,Zt,s(q(r)),1)],10,Xt)):m(``,!0)])]),y(`div`,Qt,[y(`div`,$t,[n[19]||=y(`span`,{class:`text-primary text-xs font-semibold uppercase tracking-wide`},`Path`,-1),y(`span`,en,s(Q(r)?Un(Q(r).hops):`Unknown`),1)]),Q(r)?(i(),b(`div`,tn,[(i(!0),b(v,null,t(Q(r).nodes,(e,t)=>(i(),b(v,{key:`mobile-path-${t}`},[y(`span`,{class:f([`mobile-path-chip inline-flex items-center px-2 py-1 rounded-lg text-xs font-mono font-semibold leading-tight`,t===0?`bg-badge-cyan-bg text-badge-cyan-text`:`text-content-secondary dark:text-content-muted`]),title:e},s(e),11,nn),t
0&&r.transmitted?(i(),b(`div`,{key:0,class:f([`w-1.5 h-1.5 rounded-full flex-shrink-0`,Bn(r)])},null,2)):m(``,!0),y(`span`,_n,s(Number(r.tx_delay_ms)>0?Vn(Number(r.tx_delay_ms)):`--`),1)])])])]),r.drop_reason?(i(),b(`div`,vn,s(r.drop_reason),1)):m(``,!0)])])],10,rt))),128))],2))),128))]),G.value>1?(i(),b(`div`,yn,[y(`div`,bn,[y(`span`,xn,[_(` Showing `+s((C.value-1)*X+1)+` - `+s(Math.min(C.value*X,U.value.length))+` of `+s(U.value.length)+` hash groups `,1),y(`span`,Sn,`(`+s(H.value.length)+` packets)`,1)]),xe.value?(i(),b(`div`,Cn,[n[25]||=y(`span`,{class:`text-content-secondary dark:text-content-muted text-xs`},`•`,-1),y(`button`,{onClick:Jn,disabled:k.value,class:f([`glass-card border border-primary rounded-[8px] px-3 py-1.5 text-xs transition-all duration-200 focus:outline-none focus:ring-1 focus:ring-primary/opacity-medium hover:bg-primary/opacity-light`,{"text-primary border-primary cursor-pointer":!k.value,"text-content-secondary dark:text-content-muted border-stroke-subtle dark:border-stroke cursor-not-allowed opacity-50":k.value}])},s(k.value?`Loading...`:`Load ${Math.min(200,Z-E.value)} more`),11,wn),y(`span`,Tn,`(`+s(E.value)+`/`+s(Z)+` max)`,1)])):m(``,!0)]),y(`div`,En,[y(`button`,{onClick:n[2]||=e=>--C.value,disabled:C.value<=1,class:f([`glass-card border rounded-[10px] px-3 py-2 text-sm transition-all duration-200 focus:outline-none focus:ring-1 focus:ring-primary/opacity-medium prev-next-btn`,{"border-stroke-subtle dark:border-stroke text-content-muted cursor-not-allowed opacity-50":C.value<=1,"border-stroke-subtle dark:border-stroke text-content-primary hover:border-primary dark:hover:border-primary hover:text-primary hover:bg-primary/opacity-light":C.value>1}])},[...n[26]||=[y(`span`,{class:`hidden sm:inline`},`Previous`,-1),y(`span`,{class:`sm:hidden`},`‹`,-1)]],10,Dn),y(`div`,On,[C.value>3?(i(),b(`button`,{key:0,onClick:n[3]||=e=>C.value=1,class:`glass-card border border-stroke-subtle dark:border-stroke hover:border-primary dark:hover:border-primary rounded-[8px] px-3 py-2 text-sm text-content-primary hover:text-primary hover:bg-primary/opacity-light transition-all duration-200 focus:outline-none focus:ring-1 focus:ring-primary/opacity-medium`},` 1 `)):m(``,!0),C.value>4?(i(),b(`span`,kn,`...`)):m(``,!0),(i(!0),b(v,null,t(Array.from({length:Math.min(5,G.value)},(e,t)=>Math.max(1,Math.min(C.value-2,G.value-4))+t).filter(e=>e<=G.value),e=>(i(),b(`button`,{key:e,onClick:t=>C.value=e,class:f([`glass-card border rounded-[8px] px-3 py-2 text-sm transition-all duration-200 focus:outline-none focus:ring-1 focus:ring-primary/opacity-medium page-number`,{"border-primary bg-primary/opacity-light text-primary":C.value===e,"border-stroke-subtle dark:border-stroke text-content-primary hover:border-primary dark:hover:border-primary hover:text-primary hover:bg-primary/opacity-light":C.value!==e}])},s(e),11,An))),128)),C.value
[e.rxUtil,e.txUtil]))*1.05;k.value=Math.max(5,Math.ceil(g/5)*5),Y={data:h,yAxisMax:k.value,fetchedAt:Date.now()},C.value=!1,w.value=!1,T.value=!1,D.value=null,S(()=>F())}catch(e){console.error(`Failed to fetch airtime data:`,e),v.value=[],C.value=!1,w.value=!1,T.value=!1,D.value=e instanceof Error?e.message:`Failed to load chart data`,S(()=>F())}},F=()=>{if(!p.value)return;let e=p.value,t=e.getContext(`2d`);if(!t)return;let n=e.parentElement;if(!n)return;let r=n.getBoundingClientRect(),i=r.width,a=r.height;if(e.width=i*window.devicePixelRatio,e.height=a*window.devicePixelRatio,e.style.width=i+`px`,e.style.height=a+`px`,t.scale(window.devicePixelRatio,window.devicePixelRatio),t.clearRect(0,0,i,a),C.value){t.fillStyle=u().axisLabel,t.font=`16px system-ui`,t.textAlign=`center`,t.fillText(`Loading chart data...`,i/2,a/2);return}if(v.value.length===0){t.fillStyle=u().axisLabel,t.font=`16px system-ui`,t.textAlign=`center`,t.fillText(`No data available`,i/2,a/2);return}let o=i-45-20,s=a-40,l=k.value,d=k.value,f=u();t.strokeStyle=f.gridLine,t.lineWidth=1,t.font=`10px system-ui`,t.textAlign=`right`;for(let e=0;e<=5;e++){let n=20+s*e/5;t.beginPath(),t.moveTo(45,n),t.lineTo(i-20,n),t.stroke();let r=l-e/5*d;t.fillStyle=f.axisLabel,t.fillText(`${r.toFixed(0)}%`,40,n+3)}for(let e=0;e<=6;e++){let n=45+o*e/6;t.beginPath(),t.moveTo(n,20),t.lineTo(n,a-20),t.stroke()}v.value.length>1&&(t.strokeStyle=c.rx,t.lineWidth=2,t.beginPath(),v.value.forEach((e,n)=>{let r=45+o*n/(v.value.length-1),i=a-20-Math.min(e.rxUtil,k.value)/d*s;n===0?t.moveTo(r,i):t.lineTo(r,i)}),t.stroke()),v.value.length>1&&(t.strokeStyle=c.tx,t.lineWidth=2,t.beginPath(),v.value.forEach((e,n)=>{let r=45+o*n/(v.value.length-1),i=a-20-Math.min(e.txUtil,k.value)/d*s;n===0?t.moveTo(r,i):t.lineTo(r,i)}),t.stroke())};return M(P,{intervalMs:12e4,immediate:!1}),e(()=>{Y&&Date.now()-Y.fetchedAt Airtime Utilization
je(r),[`stop`])},[n[18]||=y(`svg`,{class:`w-5 h-5`,fill:`none`,stroke:`currentColor`,viewBox:`0 0 24 24`,"aria-hidden":`true`},[y(`rect`,{x:`9`,y:`9`,width:`11`,height:`11`,rx:`2`,ry:`2`,"stroke-width":`2`}),y(`path`,{d:`M6 15H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v1`,"stroke-width":`2`,"stroke-linecap":`round`,"stroke-linejoin":`round`})],-1),y(`span`,Zt,s(q(r)),1)],10,Xt)):m(``,!0)])]),y(`div`,Qt,[y(`div`,$t,[n[19]||=y(`span`,{class:`text-primary text-xs font-semibold uppercase tracking-wide`},`Path`,-1),y(`span`,en,s(Q(r)?Un(Q(r).hops):`Unknown`),1)]),Q(r)?(i(),b(`div`,tn,[(i(!0),b(v,null,t(Q(r).nodes,(e,t)=>(i(),b(v,{key:`mobile-path-${t}`},[y(`span`,{class:f([`mobile-path-chip inline-flex items-center px-2 py-1 rounded-lg text-xs font-mono font-semibold leading-tight`,t===0?`bg-badge-cyan-bg text-badge-cyan-text`:`text-content-secondary dark:text-content-muted`]),title:e},s(e),11,nn),t
0&&r.transmitted?(i(),b(`div`,{key:0,class:f([`w-1.5 h-1.5 rounded-full flex-shrink-0`,Bn(r)])},null,2)):m(``,!0),y(`span`,_n,s(Number(r.tx_delay_ms)>0?Vn(Number(r.tx_delay_ms)):`--`),1)])])])]),r.drop_reason?(i(),b(`div`,vn,s(r.drop_reason),1)):m(``,!0)])])],10,rt))),128))],2))),128))]),G.value>1?(i(),b(`div`,yn,[y(`div`,bn,[y(`span`,xn,[_(` Showing `+s((C.value-1)*X+1)+` - `+s(Math.min(C.value*X,U.value.length))+` of `+s(U.value.length)+` hash groups `,1),y(`span`,Sn,`(`+s(H.value.length)+` packets)`,1)]),xe.value?(i(),b(`div`,Cn,[n[25]||=y(`span`,{class:`text-content-secondary dark:text-content-muted text-xs`},`•`,-1),y(`button`,{onClick:Jn,disabled:k.value,class:f([`glass-card border border-primary rounded-[8px] px-3 py-1.5 text-xs transition-all duration-200 focus:outline-none focus:ring-1 focus:ring-primary/opacity-medium hover:bg-primary/opacity-light`,{"text-primary border-primary cursor-pointer":!k.value,"text-content-secondary dark:text-content-muted border-stroke-subtle dark:border-stroke cursor-not-allowed opacity-50":k.value}])},s(k.value?`Loading...`:`Load ${Math.min(200,Z-E.value)} more`),11,wn),y(`span`,Tn,`(`+s(E.value)+`/`+s(Z)+` max)`,1)])):m(``,!0)]),y(`div`,En,[y(`button`,{onClick:n[2]||=e=>--C.value,disabled:C.value<=1,class:f([`glass-card border rounded-[10px] px-3 py-2 text-sm transition-all duration-200 focus:outline-none focus:ring-1 focus:ring-primary/opacity-medium prev-next-btn`,{"border-stroke-subtle dark:border-stroke text-content-muted cursor-not-allowed opacity-50":C.value<=1,"border-stroke-subtle dark:border-stroke text-content-primary hover:border-primary dark:hover:border-primary hover:text-primary hover:bg-primary/opacity-light":C.value>1}])},[...n[26]||=[y(`span`,{class:`hidden sm:inline`},`Previous`,-1),y(`span`,{class:`sm:hidden`},`‹`,-1)]],10,Dn),y(`div`,On,[C.value>3?(i(),b(`button`,{key:0,onClick:n[3]||=e=>C.value=1,class:`glass-card border border-stroke-subtle dark:border-stroke hover:border-primary dark:hover:border-primary rounded-[8px] px-3 py-2 text-sm text-content-primary hover:text-primary hover:bg-primary/opacity-light transition-all duration-200 focus:outline-none focus:ring-1 focus:ring-primary/opacity-medium`},` 1 `)):m(``,!0),C.value>4?(i(),b(`span`,kn,`...`)):m(``,!0),(i(!0),b(v,null,t(Array.from({length:Math.min(5,G.value)},(e,t)=>Math.max(1,Math.min(C.value-2,G.value-4))+t).filter(e=>e<=G.value),e=>(i(),b(`button`,{key:e,onClick:t=>C.value=e,class:f([`glass-card border rounded-[8px] px-3 py-2 text-sm transition-all duration-200 focus:outline-none focus:ring-1 focus:ring-primary/opacity-medium page-number`,{"border-primary bg-primary/opacity-light text-primary":C.value===e,"border-stroke-subtle dark:border-stroke text-content-primary hover:border-primary dark:hover:border-primary hover:text-primary hover:bg-primary/opacity-light":C.value!==e}])},s(e),11,An))),128)),C.value
n.far?null:{distance:l,point:Ri.clone(),object:e}}function Vi(e,t,n,r,i,a,o,s,c,l){e.getVertexPosition(s,Mi),e.getVertexPosition(c,Ni),e.getVertexPosition(l,Pi);let u=Bi(e,t,n,r,Mi,Ni,Pi,Li);if(u){let e=new q;_r.getBarycoord(Li,Mi,Ni,Pi,e),i&&(u.uv=_r.getInterpolatedAttribute(i,s,c,l,e,new K)),a&&(u.uv1=_r.getInterpolatedAttribute(a,s,c,l,e,new K)),o&&(u.normal=_r.getInterpolatedAttribute(o,s,c,l,e,new q),u.normal.dot(r.direction)>0&&u.normal.multiplyScalar(-1));let t={a:s,b:c,c:l,normal:new q,materialIndex:0};_r.getNormal(Mi,Ni,Pi,t.normal),u.face=t,u.barycoord=e}return u}var Hi=class extends hn{constructor(e=null,t=1,n=1,r,i,a,o,s,c=A,l=A,u,d){super(null,a,o,s,c,l,r,i,u,d),this.isDataTexture=!0,this.image={data:e,width:t,height:n},this.generateMipmaps=!1,this.flipY=!1,this.unpackAlignment=1}},Ui=new q,Wi=new q,Gi=new J,Ki=class{constructor(e=new q(1,0,0),t=0){this.isPlane=!0,this.normal=e,this.constant=t}set(e,t){return this.normal.copy(e),this.constant=t,this}setComponents(e,t,n,r){return this.normal.set(e,t,n),this.constant=r,this}setFromNormalAndCoplanarPoint(e,t){return this.normal.copy(e),this.constant=-t.dot(this.normal),this}setFromCoplanarPoints(e,t,n){let r=Ui.subVectors(n,t).cross(Wi.subVectors(e,t)).normalize();return this.setFromNormalAndCoplanarPoint(r,e),this}copy(e){return this.normal.copy(e.normal),this.constant=e.constant,this}normalize(){let e=1/this.normal.length();return this.normal.multiplyScalar(e),this.constant*=e,this}negate(){return this.constant*=-1,this.normal.negate(),this}distanceToPoint(e){return this.normal.dot(e)+this.constant}distanceToSphere(e){return this.distanceToPoint(e.center)-e.radius}projectPoint(e,t){return t.copy(e).addScaledVector(this.normal,-this.distanceToPoint(e))}intersectLine(e,t,n=!0){let r=e.delta(Ui),i=this.normal.dot(r);if(i===0)return this.distanceToPoint(e.start)===0?t.copy(e.start):null;let a=-(e.start.dot(this.normal)+this.constant)/i;return n===!0&&(a<0||a>1)?null:t.copy(e.start).addScaledVector(r,a)}intersectsLine(e){let t=this.distanceToPoint(e.start),n=this.distanceToPoint(e.end);return t<0&&n>0||n<0&&t>0}intersectsBox(e){return e.intersectsPlane(this)}intersectsSphere(e){return e.intersectsPlane(this)}coplanarPoint(e){return e.copy(this.normal).multiplyScalar(-this.constant)}applyMatrix4(e,t){let n=t||Gi.getNormalMatrix(e),r=this.coplanarPoint(Ui).applyMatrix4(e),i=this.normal.applyMatrix3(n).normalize();return this.constant=-r.dot(i),this}translate(e){return this.constant-=e.dot(this.normal),this}equals(e){return e.normal.equals(this.normal)&&e.constant===this.constant}clone(){return new this.constructor().copy(this)}},qi=new Ur,Ji=new K(.5,.5),Yi=new q,Xi=class{constructor(e=new Ki,t=new Ki,n=new Ki,r=new Ki,i=new Ki,a=new Ki){this.planes=[e,t,n,r,i,a]}set(e,t,n,r,i,a){let o=this.planes;return o[0].copy(e),o[1].copy(t),o[2].copy(n),o[3].copy(r),o[4].copy(i),o[5].copy(a),this}copy(e){let t=this.planes;for(let n=0;n<6;n++)t[n].copy(e.planes[n]);return this}setFromProjectionMatrix(e,t=mt,n=!1){let r=this.planes,i=e.elements,a=i[0],o=i[1],s=i[2],c=i[3],l=i[4],u=i[5],d=i[6],f=i[7],p=i[8],m=i[9],h=i[10],g=i[11],_=i[12],v=i[13],y=i[14],b=i[15];if(r[0].setComponents(c-a,f-l,g-p,b-_).normalize(),r[1].setComponents(c+a,f+l,g+p,b+_).normalize(),r[2].setComponents(c+o,f+u,g+m,b+v).normalize(),r[3].setComponents(c-o,f-u,g-m,b-v).normalize(),n)r[4].setComponents(s,d,h,y).normalize(),r[5].setComponents(c-s,f-d,g-h,b-y).normalize();else if(r[4].setComponents(c-s,f-d,g-h,b-y).normalize(),t===2e3)r[5].setComponents(c+s,f+d,g+h,b+y).normalize();else if(t===2001)r[5].setComponents(s,d,h,y).normalize();else throw Error(`THREE.Frustum.setFromProjectionMatrix(): Invalid coordinate system: `+t);return this}intersectsObject(e){if(e.boundingSphere!==void 0)e.boundingSphere===null&&e.computeBoundingSphere(),qi.copy(e.boundingSphere).applyMatrix4(e.matrixWorld);else{let t=e.geometry;t.boundingSphere===null&&t.computeBoundingSphere(),qi.copy(t.boundingSphere).applyMatrix4(e.matrixWorld)}return this.intersectsSphere(qi)}intersectsSprite(e){return qi.center.set(0,0,0),qi.radius=.7071067811865476+Ji.distanceTo(e.center),qi.applyMatrix4(e.matrixWorld),this.intersectsSphere(qi)}intersectsSphere(e){let t=this.planes,n=e.center,r=-e.radius;for(let e=0;e<6;e++)if(t[e].distanceToPoint(n)