diff --git a/404.html b/404.html index 6df223468..e09575c59 100644 --- a/404.html +++ b/404.html @@ -14,7 +14,7 @@ - + @@ -22,7 +22,7 @@ - + @@ -46,6 +46,8 @@ + + @@ -137,7 +139,7 @@
-
+
Initializing search @@ -154,7 +156,7 @@
- +
@@ -1097,7 +1196,13 @@

404 - Not found

- + + + + + + + diff --git a/assets/javascripts/bundle.8fd75fb4.min.js b/assets/javascripts/bundle.8fd75fb4.min.js deleted file mode 100644 index 2f2f130e2..000000000 --- a/assets/javascripts/bundle.8fd75fb4.min.js +++ /dev/null @@ -1,29 +0,0 @@ -"use strict";(()=>{var _i=Object.create;var br=Object.defineProperty;var Ai=Object.getOwnPropertyDescriptor;var Ci=Object.getOwnPropertyNames,Ft=Object.getOwnPropertySymbols,ki=Object.getPrototypeOf,vr=Object.prototype.hasOwnProperty,eo=Object.prototype.propertyIsEnumerable;var Zr=(e,t,r)=>t in e?br(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,F=(e,t)=>{for(var r in t||(t={}))vr.call(t,r)&&Zr(e,r,t[r]);if(Ft)for(var r of Ft(t))eo.call(t,r)&&Zr(e,r,t[r]);return e};var to=(e,t)=>{var r={};for(var o in e)vr.call(e,o)&&t.indexOf(o)<0&&(r[o]=e[o]);if(e!=null&&Ft)for(var o of Ft(e))t.indexOf(o)<0&&eo.call(e,o)&&(r[o]=e[o]);return r};var gr=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports);var Hi=(e,t,r,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of Ci(t))!vr.call(e,n)&&n!==r&&br(e,n,{get:()=>t[n],enumerable:!(o=Ai(t,n))||o.enumerable});return e};var jt=(e,t,r)=>(r=e!=null?_i(ki(e)):{},Hi(t||!e||!e.__esModule?br(r,"default",{value:e,enumerable:!0}):r,e));var ro=(e,t,r)=>new Promise((o,n)=>{var i=c=>{try{a(r.next(c))}catch(p){n(p)}},s=c=>{try{a(r.throw(c))}catch(p){n(p)}},a=c=>c.done?o(c.value):Promise.resolve(c.value).then(i,s);a((r=r.apply(e,t)).next())});var no=gr((xr,oo)=>{(function(e,t){typeof xr=="object"&&typeof oo!="undefined"?t():typeof define=="function"&&define.amd?define(t):t()})(xr,function(){"use strict";function e(r){var o=!0,n=!1,i=null,s={text:!0,search:!0,url:!0,tel:!0,email:!0,password:!0,number:!0,date:!0,month:!0,week:!0,time:!0,datetime:!0,"datetime-local":!0};function a(C){return!!(C&&C!==document&&C.nodeName!=="HTML"&&C.nodeName!=="BODY"&&"classList"in C&&"contains"in C.classList)}function c(C){var ct=C.type,Ne=C.tagName;return!!(Ne==="INPUT"&&s[ct]&&!C.readOnly||Ne==="TEXTAREA"&&!C.readOnly||C.isContentEditable)}function p(C){C.classList.contains("focus-visible")||(C.classList.add("focus-visible"),C.setAttribute("data-focus-visible-added",""))}function l(C){C.hasAttribute("data-focus-visible-added")&&(C.classList.remove("focus-visible"),C.removeAttribute("data-focus-visible-added"))}function f(C){C.metaKey||C.altKey||C.ctrlKey||(a(r.activeElement)&&p(r.activeElement),o=!0)}function u(C){o=!1}function h(C){a(C.target)&&(o||c(C.target))&&p(C.target)}function w(C){a(C.target)&&(C.target.classList.contains("focus-visible")||C.target.hasAttribute("data-focus-visible-added"))&&(n=!0,window.clearTimeout(i),i=window.setTimeout(function(){n=!1},100),l(C.target))}function A(C){document.visibilityState==="hidden"&&(n&&(o=!0),Z())}function Z(){document.addEventListener("mousemove",J),document.addEventListener("mousedown",J),document.addEventListener("mouseup",J),document.addEventListener("pointermove",J),document.addEventListener("pointerdown",J),document.addEventListener("pointerup",J),document.addEventListener("touchmove",J),document.addEventListener("touchstart",J),document.addEventListener("touchend",J)}function te(){document.removeEventListener("mousemove",J),document.removeEventListener("mousedown",J),document.removeEventListener("mouseup",J),document.removeEventListener("pointermove",J),document.removeEventListener("pointerdown",J),document.removeEventListener("pointerup",J),document.removeEventListener("touchmove",J),document.removeEventListener("touchstart",J),document.removeEventListener("touchend",J)}function J(C){C.target.nodeName&&C.target.nodeName.toLowerCase()==="html"||(o=!1,te())}document.addEventListener("keydown",f,!0),document.addEventListener("mousedown",u,!0),document.addEventListener("pointerdown",u,!0),document.addEventListener("touchstart",u,!0),document.addEventListener("visibilitychange",A,!0),Z(),r.addEventListener("focus",h,!0),r.addEventListener("blur",w,!0),r.nodeType===Node.DOCUMENT_FRAGMENT_NODE&&r.host?r.host.setAttribute("data-js-focus-visible",""):r.nodeType===Node.DOCUMENT_NODE&&(document.documentElement.classList.add("js-focus-visible"),document.documentElement.setAttribute("data-js-focus-visible",""))}if(typeof window!="undefined"&&typeof document!="undefined"){window.applyFocusVisiblePolyfill=e;var t;try{t=new CustomEvent("focus-visible-polyfill-ready")}catch(r){t=document.createEvent("CustomEvent"),t.initCustomEvent("focus-visible-polyfill-ready",!1,!1,{})}window.dispatchEvent(t)}typeof document!="undefined"&&e(document)})});var zr=gr((kt,Vr)=>{/*! - * clipboard.js v2.0.11 - * https://clipboardjs.com/ - * - * Licensed MIT © Zeno Rocha - */(function(t,r){typeof kt=="object"&&typeof Vr=="object"?Vr.exports=r():typeof define=="function"&&define.amd?define([],r):typeof kt=="object"?kt.ClipboardJS=r():t.ClipboardJS=r()})(kt,function(){return function(){var e={686:function(o,n,i){"use strict";i.d(n,{default:function(){return Li}});var s=i(279),a=i.n(s),c=i(370),p=i.n(c),l=i(817),f=i.n(l);function u(D){try{return document.execCommand(D)}catch(M){return!1}}var h=function(M){var O=f()(M);return u("cut"),O},w=h;function A(D){var M=document.documentElement.getAttribute("dir")==="rtl",O=document.createElement("textarea");O.style.fontSize="12pt",O.style.border="0",O.style.padding="0",O.style.margin="0",O.style.position="absolute",O.style[M?"right":"left"]="-9999px";var I=window.pageYOffset||document.documentElement.scrollTop;return O.style.top="".concat(I,"px"),O.setAttribute("readonly",""),O.value=D,O}var Z=function(M,O){var I=A(M);O.container.appendChild(I);var W=f()(I);return u("copy"),I.remove(),W},te=function(M){var O=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{container:document.body},I="";return typeof M=="string"?I=Z(M,O):M instanceof HTMLInputElement&&!["text","search","url","tel","password"].includes(M==null?void 0:M.type)?I=Z(M.value,O):(I=f()(M),u("copy")),I},J=te;function C(D){"@babel/helpers - typeof";return typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?C=function(O){return typeof O}:C=function(O){return O&&typeof Symbol=="function"&&O.constructor===Symbol&&O!==Symbol.prototype?"symbol":typeof O},C(D)}var ct=function(){var M=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{},O=M.action,I=O===void 0?"copy":O,W=M.container,K=M.target,Ce=M.text;if(I!=="copy"&&I!=="cut")throw new Error('Invalid "action" value, use either "copy" or "cut"');if(K!==void 0)if(K&&C(K)==="object"&&K.nodeType===1){if(I==="copy"&&K.hasAttribute("disabled"))throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute');if(I==="cut"&&(K.hasAttribute("readonly")||K.hasAttribute("disabled")))throw new Error(`Invalid "target" attribute. You can't cut text from elements with "readonly" or "disabled" attributes`)}else throw new Error('Invalid "target" value, use a valid Element');if(Ce)return J(Ce,{container:W});if(K)return I==="cut"?w(K):J(K,{container:W})},Ne=ct;function Pe(D){"@babel/helpers - typeof";return typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?Pe=function(O){return typeof O}:Pe=function(O){return O&&typeof Symbol=="function"&&O.constructor===Symbol&&O!==Symbol.prototype?"symbol":typeof O},Pe(D)}function xi(D,M){if(!(D instanceof M))throw new TypeError("Cannot call a class as a function")}function Xr(D,M){for(var O=0;O0&&arguments[0]!==void 0?arguments[0]:{};this.action=typeof W.action=="function"?W.action:this.defaultAction,this.target=typeof W.target=="function"?W.target:this.defaultTarget,this.text=typeof W.text=="function"?W.text:this.defaultText,this.container=Pe(W.container)==="object"?W.container:document.body}},{key:"listenClick",value:function(W){var K=this;this.listener=p()(W,"click",function(Ce){return K.onClick(Ce)})}},{key:"onClick",value:function(W){var K=W.delegateTarget||W.currentTarget,Ce=this.action(K)||"copy",It=Ne({action:Ce,container:this.container,target:this.target(K),text:this.text(K)});this.emit(It?"success":"error",{action:Ce,text:It,trigger:K,clearSelection:function(){K&&K.focus(),window.getSelection().removeAllRanges()}})}},{key:"defaultAction",value:function(W){return hr("action",W)}},{key:"defaultTarget",value:function(W){var K=hr("target",W);if(K)return document.querySelector(K)}},{key:"defaultText",value:function(W){return hr("text",W)}},{key:"destroy",value:function(){this.listener.destroy()}}],[{key:"copy",value:function(W){var K=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{container:document.body};return J(W,K)}},{key:"cut",value:function(W){return w(W)}},{key:"isSupported",value:function(){var W=arguments.length>0&&arguments[0]!==void 0?arguments[0]:["copy","cut"],K=typeof W=="string"?[W]:W,Ce=!!document.queryCommandSupported;return K.forEach(function(It){Ce=Ce&&!!document.queryCommandSupported(It)}),Ce}}]),O}(a()),Li=Mi},828:function(o){var n=9;if(typeof Element!="undefined"&&!Element.prototype.matches){var i=Element.prototype;i.matches=i.matchesSelector||i.mozMatchesSelector||i.msMatchesSelector||i.oMatchesSelector||i.webkitMatchesSelector}function s(a,c){for(;a&&a.nodeType!==n;){if(typeof a.matches=="function"&&a.matches(c))return a;a=a.parentNode}}o.exports=s},438:function(o,n,i){var s=i(828);function a(l,f,u,h,w){var A=p.apply(this,arguments);return l.addEventListener(u,A,w),{destroy:function(){l.removeEventListener(u,A,w)}}}function c(l,f,u,h,w){return typeof l.addEventListener=="function"?a.apply(null,arguments):typeof u=="function"?a.bind(null,document).apply(null,arguments):(typeof l=="string"&&(l=document.querySelectorAll(l)),Array.prototype.map.call(l,function(A){return a(A,f,u,h,w)}))}function p(l,f,u,h){return function(w){w.delegateTarget=s(w.target,f),w.delegateTarget&&h.call(l,w)}}o.exports=c},879:function(o,n){n.node=function(i){return i!==void 0&&i instanceof HTMLElement&&i.nodeType===1},n.nodeList=function(i){var s=Object.prototype.toString.call(i);return i!==void 0&&(s==="[object NodeList]"||s==="[object HTMLCollection]")&&"length"in i&&(i.length===0||n.node(i[0]))},n.string=function(i){return typeof i=="string"||i instanceof String},n.fn=function(i){var s=Object.prototype.toString.call(i);return s==="[object Function]"}},370:function(o,n,i){var s=i(879),a=i(438);function c(u,h,w){if(!u&&!h&&!w)throw new Error("Missing required arguments");if(!s.string(h))throw new TypeError("Second argument must be a String");if(!s.fn(w))throw new TypeError("Third argument must be a Function");if(s.node(u))return p(u,h,w);if(s.nodeList(u))return l(u,h,w);if(s.string(u))return f(u,h,w);throw new TypeError("First argument must be a String, HTMLElement, HTMLCollection, or NodeList")}function p(u,h,w){return u.addEventListener(h,w),{destroy:function(){u.removeEventListener(h,w)}}}function l(u,h,w){return Array.prototype.forEach.call(u,function(A){A.addEventListener(h,w)}),{destroy:function(){Array.prototype.forEach.call(u,function(A){A.removeEventListener(h,w)})}}}function f(u,h,w){return a(document.body,u,h,w)}o.exports=c},817:function(o){function n(i){var s;if(i.nodeName==="SELECT")i.focus(),s=i.value;else if(i.nodeName==="INPUT"||i.nodeName==="TEXTAREA"){var a=i.hasAttribute("readonly");a||i.setAttribute("readonly",""),i.select(),i.setSelectionRange(0,i.value.length),a||i.removeAttribute("readonly"),s=i.value}else{i.hasAttribute("contenteditable")&&i.focus();var c=window.getSelection(),p=document.createRange();p.selectNodeContents(i),c.removeAllRanges(),c.addRange(p),s=c.toString()}return s}o.exports=n},279:function(o){function n(){}n.prototype={on:function(i,s,a){var c=this.e||(this.e={});return(c[i]||(c[i]=[])).push({fn:s,ctx:a}),this},once:function(i,s,a){var c=this;function p(){c.off(i,p),s.apply(a,arguments)}return p._=s,this.on(i,p,a)},emit:function(i){var s=[].slice.call(arguments,1),a=((this.e||(this.e={}))[i]||[]).slice(),c=0,p=a.length;for(c;c{"use strict";/*! - * escape-html - * Copyright(c) 2012-2013 TJ Holowaychuk - * Copyright(c) 2015 Andreas Lubbe - * Copyright(c) 2015 Tiancheng "Timothy" Gu - * MIT Licensed - */var Va=/["'&<>]/;qn.exports=za;function za(e){var t=""+e,r=Va.exec(t);if(!r)return t;var o,n="",i=0,s=0;for(i=r.index;i0&&i[i.length-1])&&(p[0]===6||p[0]===2)){r=0;continue}if(p[0]===3&&(!i||p[1]>i[0]&&p[1]=e.length&&(e=void 0),{value:e&&e[o++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")}function V(e,t){var r=typeof Symbol=="function"&&e[Symbol.iterator];if(!r)return e;var o=r.call(e),n,i=[],s;try{for(;(t===void 0||t-- >0)&&!(n=o.next()).done;)i.push(n.value)}catch(a){s={error:a}}finally{try{n&&!n.done&&(r=o.return)&&r.call(o)}finally{if(s)throw s.error}}return i}function z(e,t,r){if(r||arguments.length===2)for(var o=0,n=t.length,i;o1||a(u,h)})})}function a(u,h){try{c(o[u](h))}catch(w){f(i[0][3],w)}}function c(u){u.value instanceof ot?Promise.resolve(u.value.v).then(p,l):f(i[0][2],u)}function p(u){a("next",u)}function l(u){a("throw",u)}function f(u,h){u(h),i.shift(),i.length&&a(i[0][0],i[0][1])}}function so(e){if(!Symbol.asyncIterator)throw new TypeError("Symbol.asyncIterator is not defined.");var t=e[Symbol.asyncIterator],r;return t?t.call(e):(e=typeof ue=="function"?ue(e):e[Symbol.iterator](),r={},o("next"),o("throw"),o("return"),r[Symbol.asyncIterator]=function(){return this},r);function o(i){r[i]=e[i]&&function(s){return new Promise(function(a,c){s=e[i](s),n(a,c,s.done,s.value)})}}function n(i,s,a,c){Promise.resolve(c).then(function(p){i({value:p,done:a})},s)}}function k(e){return typeof e=="function"}function pt(e){var t=function(o){Error.call(o),o.stack=new Error().stack},r=e(t);return r.prototype=Object.create(Error.prototype),r.prototype.constructor=r,r}var Wt=pt(function(e){return function(r){e(this),this.message=r?r.length+` errors occurred during unsubscription: -`+r.map(function(o,n){return n+1+") "+o.toString()}).join(` - `):"",this.name="UnsubscriptionError",this.errors=r}});function Ve(e,t){if(e){var r=e.indexOf(t);0<=r&&e.splice(r,1)}}var Ie=function(){function e(t){this.initialTeardown=t,this.closed=!1,this._parentage=null,this._finalizers=null}return e.prototype.unsubscribe=function(){var t,r,o,n,i;if(!this.closed){this.closed=!0;var s=this._parentage;if(s)if(this._parentage=null,Array.isArray(s))try{for(var a=ue(s),c=a.next();!c.done;c=a.next()){var p=c.value;p.remove(this)}}catch(A){t={error:A}}finally{try{c&&!c.done&&(r=a.return)&&r.call(a)}finally{if(t)throw t.error}}else s.remove(this);var l=this.initialTeardown;if(k(l))try{l()}catch(A){i=A instanceof Wt?A.errors:[A]}var f=this._finalizers;if(f){this._finalizers=null;try{for(var u=ue(f),h=u.next();!h.done;h=u.next()){var w=h.value;try{co(w)}catch(A){i=i!=null?i:[],A instanceof Wt?i=z(z([],V(i)),V(A.errors)):i.push(A)}}}catch(A){o={error:A}}finally{try{h&&!h.done&&(n=u.return)&&n.call(u)}finally{if(o)throw o.error}}}if(i)throw new Wt(i)}},e.prototype.add=function(t){var r;if(t&&t!==this)if(this.closed)co(t);else{if(t instanceof e){if(t.closed||t._hasParent(this))return;t._addParent(this)}(this._finalizers=(r=this._finalizers)!==null&&r!==void 0?r:[]).push(t)}},e.prototype._hasParent=function(t){var r=this._parentage;return r===t||Array.isArray(r)&&r.includes(t)},e.prototype._addParent=function(t){var r=this._parentage;this._parentage=Array.isArray(r)?(r.push(t),r):r?[r,t]:t},e.prototype._removeParent=function(t){var r=this._parentage;r===t?this._parentage=null:Array.isArray(r)&&Ve(r,t)},e.prototype.remove=function(t){var r=this._finalizers;r&&Ve(r,t),t instanceof e&&t._removeParent(this)},e.EMPTY=function(){var t=new e;return t.closed=!0,t}(),e}();var Er=Ie.EMPTY;function Dt(e){return e instanceof Ie||e&&"closed"in e&&k(e.remove)&&k(e.add)&&k(e.unsubscribe)}function co(e){k(e)?e():e.unsubscribe()}var ke={onUnhandledError:null,onStoppedNotification:null,Promise:void 0,useDeprecatedSynchronousErrorHandling:!1,useDeprecatedNextContext:!1};var lt={setTimeout:function(e,t){for(var r=[],o=2;o0},enumerable:!1,configurable:!0}),t.prototype._trySubscribe=function(r){return this._throwIfClosed(),e.prototype._trySubscribe.call(this,r)},t.prototype._subscribe=function(r){return this._throwIfClosed(),this._checkFinalizedStatuses(r),this._innerSubscribe(r)},t.prototype._innerSubscribe=function(r){var o=this,n=this,i=n.hasError,s=n.isStopped,a=n.observers;return i||s?Er:(this.currentObservers=null,a.push(r),new Ie(function(){o.currentObservers=null,Ve(a,r)}))},t.prototype._checkFinalizedStatuses=function(r){var o=this,n=o.hasError,i=o.thrownError,s=o.isStopped;n?r.error(i):s&&r.complete()},t.prototype.asObservable=function(){var r=new j;return r.source=this,r},t.create=function(r,o){return new vo(r,o)},t}(j);var vo=function(e){se(t,e);function t(r,o){var n=e.call(this)||this;return n.destination=r,n.source=o,n}return t.prototype.next=function(r){var o,n;(n=(o=this.destination)===null||o===void 0?void 0:o.next)===null||n===void 0||n.call(o,r)},t.prototype.error=function(r){var o,n;(n=(o=this.destination)===null||o===void 0?void 0:o.error)===null||n===void 0||n.call(o,r)},t.prototype.complete=function(){var r,o;(o=(r=this.destination)===null||r===void 0?void 0:r.complete)===null||o===void 0||o.call(r)},t.prototype._subscribe=function(r){var o,n;return(n=(o=this.source)===null||o===void 0?void 0:o.subscribe(r))!==null&&n!==void 0?n:Er},t}(v);var St={now:function(){return(St.delegate||Date).now()},delegate:void 0};var Ot=function(e){se(t,e);function t(r,o,n){r===void 0&&(r=1/0),o===void 0&&(o=1/0),n===void 0&&(n=St);var i=e.call(this)||this;return i._bufferSize=r,i._windowTime=o,i._timestampProvider=n,i._buffer=[],i._infiniteTimeWindow=!0,i._infiniteTimeWindow=o===1/0,i._bufferSize=Math.max(1,r),i._windowTime=Math.max(1,o),i}return t.prototype.next=function(r){var o=this,n=o.isStopped,i=o._buffer,s=o._infiniteTimeWindow,a=o._timestampProvider,c=o._windowTime;n||(i.push(r),!s&&i.push(a.now()+c)),this._trimBuffer(),e.prototype.next.call(this,r)},t.prototype._subscribe=function(r){this._throwIfClosed(),this._trimBuffer();for(var o=this._innerSubscribe(r),n=this,i=n._infiniteTimeWindow,s=n._buffer,a=s.slice(),c=0;c0?e.prototype.requestAsyncId.call(this,r,o,n):(r.actions.push(this),r._scheduled||(r._scheduled=ut.requestAnimationFrame(function(){return r.flush(void 0)})))},t.prototype.recycleAsyncId=function(r,o,n){var i;if(n===void 0&&(n=0),n!=null?n>0:this.delay>0)return e.prototype.recycleAsyncId.call(this,r,o,n);var s=r.actions;o!=null&&((i=s[s.length-1])===null||i===void 0?void 0:i.id)!==o&&(ut.cancelAnimationFrame(o),r._scheduled=void 0)},t}(zt);var yo=function(e){se(t,e);function t(){return e!==null&&e.apply(this,arguments)||this}return t.prototype.flush=function(r){this._active=!0;var o=this._scheduled;this._scheduled=void 0;var n=this.actions,i;r=r||n.shift();do if(i=r.execute(r.state,r.delay))break;while((r=n[0])&&r.id===o&&n.shift());if(this._active=!1,i){for(;(r=n[0])&&r.id===o&&n.shift();)r.unsubscribe();throw i}},t}(qt);var de=new yo(xo);var L=new j(function(e){return e.complete()});function Kt(e){return e&&k(e.schedule)}function _r(e){return e[e.length-1]}function Je(e){return k(_r(e))?e.pop():void 0}function Ae(e){return Kt(_r(e))?e.pop():void 0}function Qt(e,t){return typeof _r(e)=="number"?e.pop():t}var dt=function(e){return e&&typeof e.length=="number"&&typeof e!="function"};function Yt(e){return k(e==null?void 0:e.then)}function Bt(e){return k(e[ft])}function Gt(e){return Symbol.asyncIterator&&k(e==null?void 0:e[Symbol.asyncIterator])}function Jt(e){return new TypeError("You provided "+(e!==null&&typeof e=="object"?"an invalid object":"'"+e+"'")+" where a stream was expected. You can provide an Observable, Promise, ReadableStream, Array, AsyncIterable, or Iterable.")}function Di(){return typeof Symbol!="function"||!Symbol.iterator?"@@iterator":Symbol.iterator}var Xt=Di();function Zt(e){return k(e==null?void 0:e[Xt])}function er(e){return ao(this,arguments,function(){var r,o,n,i;return Ut(this,function(s){switch(s.label){case 0:r=e.getReader(),s.label=1;case 1:s.trys.push([1,,9,10]),s.label=2;case 2:return[4,ot(r.read())];case 3:return o=s.sent(),n=o.value,i=o.done,i?[4,ot(void 0)]:[3,5];case 4:return[2,s.sent()];case 5:return[4,ot(n)];case 6:return[4,s.sent()];case 7:return s.sent(),[3,2];case 8:return[3,10];case 9:return r.releaseLock(),[7];case 10:return[2]}})})}function tr(e){return k(e==null?void 0:e.getReader)}function N(e){if(e instanceof j)return e;if(e!=null){if(Bt(e))return Ni(e);if(dt(e))return Vi(e);if(Yt(e))return zi(e);if(Gt(e))return Eo(e);if(Zt(e))return qi(e);if(tr(e))return Ki(e)}throw Jt(e)}function Ni(e){return new j(function(t){var r=e[ft]();if(k(r.subscribe))return r.subscribe(t);throw new TypeError("Provided object does not correctly implement Symbol.observable")})}function Vi(e){return new j(function(t){for(var r=0;r=2;return function(o){return o.pipe(e?g(function(n,i){return e(n,i,o)}):ce,ye(1),r?Qe(t):jo(function(){return new or}))}}function $r(e){return e<=0?function(){return L}:x(function(t,r){var o=[];t.subscribe(S(r,function(n){o.push(n),e=2,!0))}function le(e){e===void 0&&(e={});var t=e.connector,r=t===void 0?function(){return new v}:t,o=e.resetOnError,n=o===void 0?!0:o,i=e.resetOnComplete,s=i===void 0?!0:i,a=e.resetOnRefCountZero,c=a===void 0?!0:a;return function(p){var l,f,u,h=0,w=!1,A=!1,Z=function(){f==null||f.unsubscribe(),f=void 0},te=function(){Z(),l=u=void 0,w=A=!1},J=function(){var C=l;te(),C==null||C.unsubscribe()};return x(function(C,ct){h++,!A&&!w&&Z();var Ne=u=u!=null?u:r();ct.add(function(){h--,h===0&&!A&&!w&&(f=Pr(J,c))}),Ne.subscribe(ct),!l&&h>0&&(l=new it({next:function(Pe){return Ne.next(Pe)},error:function(Pe){A=!0,Z(),f=Pr(te,n,Pe),Ne.error(Pe)},complete:function(){w=!0,Z(),f=Pr(te,s),Ne.complete()}}),N(C).subscribe(l))})(p)}}function Pr(e,t){for(var r=[],o=2;oe.next(document)),e}function R(e,t=document){return Array.from(t.querySelectorAll(e))}function P(e,t=document){let r=me(e,t);if(typeof r=="undefined")throw new ReferenceError(`Missing element: expected "${e}" to be present`);return r}function me(e,t=document){return t.querySelector(e)||void 0}function Re(){var e,t,r,o;return(o=(r=(t=(e=document.activeElement)==null?void 0:e.shadowRoot)==null?void 0:t.activeElement)!=null?r:document.activeElement)!=null?o:void 0}var la=T(d(document.body,"focusin"),d(document.body,"focusout")).pipe(be(1),q(void 0),m(()=>Re()||document.body),B(1));function vt(e){return la.pipe(m(t=>e.contains(t)),Y())}function Vo(e,t){return T(d(e,"mouseenter").pipe(m(()=>!0)),d(e,"mouseleave").pipe(m(()=>!1))).pipe(t?be(t):ce,q(!1))}function Ue(e){return{x:e.offsetLeft,y:e.offsetTop}}function zo(e){return T(d(window,"load"),d(window,"resize")).pipe(Me(0,de),m(()=>Ue(e)),q(Ue(e)))}function ir(e){return{x:e.scrollLeft,y:e.scrollTop}}function et(e){return T(d(e,"scroll"),d(window,"resize")).pipe(Me(0,de),m(()=>ir(e)),q(ir(e)))}function qo(e,t){if(typeof t=="string"||typeof t=="number")e.innerHTML+=t.toString();else if(t instanceof Node)e.appendChild(t);else if(Array.isArray(t))for(let r of t)qo(e,r)}function E(e,t,...r){let o=document.createElement(e);if(t)for(let n of Object.keys(t))typeof t[n]!="undefined"&&(typeof t[n]!="boolean"?o.setAttribute(n,t[n]):o.setAttribute(n,""));for(let n of r)qo(o,n);return o}function ar(e){if(e>999){let t=+((e-950)%1e3>99);return`${((e+1e-6)/1e3).toFixed(t)}k`}else return e.toString()}function gt(e){let t=E("script",{src:e});return H(()=>(document.head.appendChild(t),T(d(t,"load"),d(t,"error").pipe(b(()=>Ar(()=>new ReferenceError(`Invalid script: ${e}`))))).pipe(m(()=>{}),_(()=>document.head.removeChild(t)),ye(1))))}var Ko=new v,ma=H(()=>typeof ResizeObserver=="undefined"?gt("https://unpkg.com/resize-observer-polyfill"):$(void 0)).pipe(m(()=>new ResizeObserver(e=>{for(let t of e)Ko.next(t)})),b(e=>T(qe,$(e)).pipe(_(()=>e.disconnect()))),B(1));function pe(e){return{width:e.offsetWidth,height:e.offsetHeight}}function Ee(e){return ma.pipe(y(t=>t.observe(e)),b(t=>Ko.pipe(g(({target:r})=>r===e),_(()=>t.unobserve(e)),m(()=>pe(e)))),q(pe(e)))}function xt(e){return{width:e.scrollWidth,height:e.scrollHeight}}function sr(e){let t=e.parentElement;for(;t&&(e.scrollWidth<=t.scrollWidth&&e.scrollHeight<=t.scrollHeight);)t=(e=t).parentElement;return t?e:void 0}var Qo=new v,fa=H(()=>$(new IntersectionObserver(e=>{for(let t of e)Qo.next(t)},{threshold:0}))).pipe(b(e=>T(qe,$(e)).pipe(_(()=>e.disconnect()))),B(1));function yt(e){return fa.pipe(y(t=>t.observe(e)),b(t=>Qo.pipe(g(({target:r})=>r===e),_(()=>t.unobserve(e)),m(({isIntersecting:r})=>r))))}function Yo(e,t=16){return et(e).pipe(m(({y:r})=>{let o=pe(e),n=xt(e);return r>=n.height-o.height-t}),Y())}var cr={drawer:P("[data-md-toggle=drawer]"),search:P("[data-md-toggle=search]")};function Bo(e){return cr[e].checked}function Be(e,t){cr[e].checked!==t&&cr[e].click()}function We(e){let t=cr[e];return d(t,"change").pipe(m(()=>t.checked),q(t.checked))}function ua(e,t){switch(e.constructor){case HTMLInputElement:return e.type==="radio"?/^Arrow/.test(t):!0;case HTMLSelectElement:case HTMLTextAreaElement:return!0;default:return e.isContentEditable}}function da(){return T(d(window,"compositionstart").pipe(m(()=>!0)),d(window,"compositionend").pipe(m(()=>!1))).pipe(q(!1))}function Go(){let e=d(window,"keydown").pipe(g(t=>!(t.metaKey||t.ctrlKey)),m(t=>({mode:Bo("search")?"search":"global",type:t.key,claim(){t.preventDefault(),t.stopPropagation()}})),g(({mode:t,type:r})=>{if(t==="global"){let o=Re();if(typeof o!="undefined")return!ua(o,r)}return!0}),le());return da().pipe(b(t=>t?L:e))}function ve(){return new URL(location.href)}function st(e,t=!1){if(G("navigation.instant")&&!t){let r=E("a",{href:e.href});document.body.appendChild(r),r.click(),r.remove()}else location.href=e.href}function Jo(){return new v}function Xo(){return location.hash.slice(1)}function Zo(e){let t=E("a",{href:e});t.addEventListener("click",r=>r.stopPropagation()),t.click()}function ha(e){return T(d(window,"hashchange"),e).pipe(m(Xo),q(Xo()),g(t=>t.length>0),B(1))}function en(e){return ha(e).pipe(m(t=>me(`[id="${t}"]`)),g(t=>typeof t!="undefined"))}function At(e){let t=matchMedia(e);return nr(r=>t.addListener(()=>r(t.matches))).pipe(q(t.matches))}function tn(){let e=matchMedia("print");return T(d(window,"beforeprint").pipe(m(()=>!0)),d(window,"afterprint").pipe(m(()=>!1))).pipe(q(e.matches))}function Ur(e,t){return e.pipe(b(r=>r?t():L))}function Wr(e,t){return new j(r=>{let o=new XMLHttpRequest;return o.open("GET",`${e}`),o.responseType="blob",o.addEventListener("load",()=>{o.status>=200&&o.status<300?(r.next(o.response),r.complete()):r.error(new Error(o.statusText))}),o.addEventListener("error",()=>{r.error(new Error("Network error"))}),o.addEventListener("abort",()=>{r.complete()}),typeof(t==null?void 0:t.progress$)!="undefined"&&(o.addEventListener("progress",n=>{var i;if(n.lengthComputable)t.progress$.next(n.loaded/n.total*100);else{let s=(i=o.getResponseHeader("Content-Length"))!=null?i:0;t.progress$.next(n.loaded/+s*100)}}),t.progress$.next(5)),o.send(),()=>o.abort()})}function De(e,t){return Wr(e,t).pipe(b(r=>r.text()),m(r=>JSON.parse(r)),B(1))}function rn(e,t){let r=new DOMParser;return Wr(e,t).pipe(b(o=>o.text()),m(o=>r.parseFromString(o,"text/html")),B(1))}function on(e,t){let r=new DOMParser;return Wr(e,t).pipe(b(o=>o.text()),m(o=>r.parseFromString(o,"text/xml")),B(1))}function nn(){return{x:Math.max(0,scrollX),y:Math.max(0,scrollY)}}function an(){return T(d(window,"scroll",{passive:!0}),d(window,"resize",{passive:!0})).pipe(m(nn),q(nn()))}function sn(){return{width:innerWidth,height:innerHeight}}function cn(){return d(window,"resize",{passive:!0}).pipe(m(sn),q(sn()))}function pn(){return Q([an(),cn()]).pipe(m(([e,t])=>({offset:e,size:t})),B(1))}function pr(e,{viewport$:t,header$:r}){let o=t.pipe(X("size")),n=Q([o,r]).pipe(m(()=>Ue(e)));return Q([r,t,n]).pipe(m(([{height:i},{offset:s,size:a},{x:c,y:p}])=>({offset:{x:s.x-c,y:s.y-p+i},size:a})))}function ba(e){return d(e,"message",t=>t.data)}function va(e){let t=new v;return t.subscribe(r=>e.postMessage(r)),t}function ln(e,t=new Worker(e)){let r=ba(t),o=va(t),n=new v;n.subscribe(o);let i=o.pipe(ee(),oe(!0));return n.pipe(ee(),$e(r.pipe(U(i))),le())}var ga=P("#__config"),Et=JSON.parse(ga.textContent);Et.base=`${new URL(Et.base,ve())}`;function we(){return Et}function G(e){return Et.features.includes(e)}function ge(e,t){return typeof t!="undefined"?Et.translations[e].replace("#",t.toString()):Et.translations[e]}function Te(e,t=document){return P(`[data-md-component=${e}]`,t)}function ne(e,t=document){return R(`[data-md-component=${e}]`,t)}function xa(e){let t=P(".md-typeset > :first-child",e);return d(t,"click",{once:!0}).pipe(m(()=>P(".md-typeset",e)),m(r=>({hash:__md_hash(r.innerHTML)})))}function mn(e){if(!G("announce.dismiss")||!e.childElementCount)return L;if(!e.hidden){let t=P(".md-typeset",e);__md_hash(t.innerHTML)===__md_get("__announce")&&(e.hidden=!0)}return H(()=>{let t=new v;return t.subscribe(({hash:r})=>{e.hidden=!0,__md_set("__announce",r)}),xa(e).pipe(y(r=>t.next(r)),_(()=>t.complete()),m(r=>F({ref:e},r)))})}function ya(e,{target$:t}){return t.pipe(m(r=>({hidden:r!==e})))}function fn(e,t){let r=new v;return r.subscribe(({hidden:o})=>{e.hidden=o}),ya(e,t).pipe(y(o=>r.next(o)),_(()=>r.complete()),m(o=>F({ref:e},o)))}function Ct(e,t){return t==="inline"?E("div",{class:"md-tooltip md-tooltip--inline",id:e,role:"tooltip"},E("div",{class:"md-tooltip__inner md-typeset"})):E("div",{class:"md-tooltip",id:e,role:"tooltip"},E("div",{class:"md-tooltip__inner md-typeset"}))}function un(e,t){if(t=t?`${t}_annotation_${e}`:void 0,t){let r=t?`#${t}`:void 0;return E("aside",{class:"md-annotation",tabIndex:0},Ct(t),E("a",{href:r,class:"md-annotation__index",tabIndex:-1},E("span",{"data-md-annotation-id":e})))}else return E("aside",{class:"md-annotation",tabIndex:0},Ct(t),E("span",{class:"md-annotation__index",tabIndex:-1},E("span",{"data-md-annotation-id":e})))}function dn(e){return E("button",{class:"md-clipboard md-icon",title:ge("clipboard.copy"),"data-clipboard-target":`#${e} > code`})}function Dr(e,t){let r=t&2,o=t&1,n=Object.keys(e.terms).filter(c=>!e.terms[c]).reduce((c,p)=>[...c,E("del",null,p)," "],[]).slice(0,-1),i=we(),s=new URL(e.location,i.base);G("search.highlight")&&s.searchParams.set("h",Object.entries(e.terms).filter(([,c])=>c).reduce((c,[p])=>`${c} ${p}`.trim(),""));let{tags:a}=we();return E("a",{href:`${s}`,class:"md-search-result__link",tabIndex:-1},E("article",{class:"md-search-result__article md-typeset","data-md-score":e.score.toFixed(2)},r>0&&E("div",{class:"md-search-result__icon md-icon"}),r>0&&E("h1",null,e.title),r<=0&&E("h2",null,e.title),o>0&&e.text.length>0&&e.text,e.tags&&e.tags.map(c=>{let p=a?c in a?`md-tag-icon md-tag--${a[c]}`:"md-tag-icon":"";return E("span",{class:`md-tag ${p}`},c)}),o>0&&n.length>0&&E("p",{class:"md-search-result__terms"},ge("search.result.term.missing"),": ",...n)))}function hn(e){let t=e[0].score,r=[...e],o=we(),n=r.findIndex(l=>!`${new URL(l.location,o.base)}`.includes("#")),[i]=r.splice(n,1),s=r.findIndex(l=>l.scoreDr(l,1)),...c.length?[E("details",{class:"md-search-result__more"},E("summary",{tabIndex:-1},E("div",null,c.length>0&&c.length===1?ge("search.result.more.one"):ge("search.result.more.other",c.length))),...c.map(l=>Dr(l,1)))]:[]];return E("li",{class:"md-search-result__item"},p)}function bn(e){return E("ul",{class:"md-source__facts"},Object.entries(e).map(([t,r])=>E("li",{class:`md-source__fact md-source__fact--${t}`},typeof r=="number"?ar(r):r)))}function Nr(e){let t=`tabbed-control tabbed-control--${e}`;return E("div",{class:t,hidden:!0},E("button",{class:"tabbed-button",tabIndex:-1,"aria-hidden":"true"}))}function vn(e){return E("div",{class:"md-typeset__scrollwrap"},E("div",{class:"md-typeset__table"},e))}function Ea(e){let t=we(),r=new URL(`../${e.version}/`,t.base);return E("li",{class:"md-version__item"},E("a",{href:`${r}`,class:"md-version__link"},e.title))}function gn(e,t){return E("div",{class:"md-version"},E("button",{class:"md-version__current","aria-label":ge("select.version")},t.title),E("ul",{class:"md-version__list"},e.map(Ea)))}var wa=0;function Ta(e,t){document.body.append(e);let{width:r}=pe(e);e.style.setProperty("--md-tooltip-width",`${r}px`),e.remove();let o=sr(t),n=typeof o!="undefined"?et(o):$({x:0,y:0}),i=T(vt(t),Vo(t)).pipe(Y());return Q([i,n]).pipe(m(([s,a])=>{let{x:c,y:p}=Ue(t),l=pe(t),f=t.closest("table");return f&&t.parentElement&&(c+=f.offsetLeft+t.parentElement.offsetLeft,p+=f.offsetTop+t.parentElement.offsetTop),{active:s,offset:{x:c-a.x+l.width/2-r/2,y:p-a.y+l.height+8}}}))}function Ge(e){let t=e.title;if(!t.length)return L;let r=`__tooltip_${wa++}`,o=Ct(r,"inline"),n=P(".md-typeset",o);return n.innerHTML=t,H(()=>{let i=new v;return i.subscribe({next({offset:s}){o.style.setProperty("--md-tooltip-x",`${s.x}px`),o.style.setProperty("--md-tooltip-y",`${s.y}px`)},complete(){o.style.removeProperty("--md-tooltip-x"),o.style.removeProperty("--md-tooltip-y")}}),T(i.pipe(g(({active:s})=>s)),i.pipe(be(250),g(({active:s})=>!s))).subscribe({next({active:s}){s?(e.insertAdjacentElement("afterend",o),e.setAttribute("aria-describedby",r),e.removeAttribute("title")):(o.remove(),e.removeAttribute("aria-describedby"),e.setAttribute("title",t))},complete(){o.remove(),e.removeAttribute("aria-describedby"),e.setAttribute("title",t)}}),i.pipe(Me(16,de)).subscribe(({active:s})=>{o.classList.toggle("md-tooltip--active",s)}),i.pipe(_t(125,de),g(()=>!!e.offsetParent),m(()=>e.offsetParent.getBoundingClientRect()),m(({x:s})=>s)).subscribe({next(s){s?o.style.setProperty("--md-tooltip-0",`${-s}px`):o.style.removeProperty("--md-tooltip-0")},complete(){o.style.removeProperty("--md-tooltip-0")}}),Ta(o,e).pipe(y(s=>i.next(s)),_(()=>i.complete()),m(s=>F({ref:e},s)))}).pipe(ze(ie))}function Sa(e,t){let r=H(()=>Q([zo(e),et(t)])).pipe(m(([{x:o,y:n},i])=>{let{width:s,height:a}=pe(e);return{x:o-i.x+s/2,y:n-i.y+a/2}}));return vt(e).pipe(b(o=>r.pipe(m(n=>({active:o,offset:n})),ye(+!o||1/0))))}function xn(e,t,{target$:r}){let[o,n]=Array.from(e.children);return H(()=>{let i=new v,s=i.pipe(ee(),oe(!0));return i.subscribe({next({offset:a}){e.style.setProperty("--md-tooltip-x",`${a.x}px`),e.style.setProperty("--md-tooltip-y",`${a.y}px`)},complete(){e.style.removeProperty("--md-tooltip-x"),e.style.removeProperty("--md-tooltip-y")}}),yt(e).pipe(U(s)).subscribe(a=>{e.toggleAttribute("data-md-visible",a)}),T(i.pipe(g(({active:a})=>a)),i.pipe(be(250),g(({active:a})=>!a))).subscribe({next({active:a}){a?e.prepend(o):o.remove()},complete(){e.prepend(o)}}),i.pipe(Me(16,de)).subscribe(({active:a})=>{o.classList.toggle("md-tooltip--active",a)}),i.pipe(_t(125,de),g(()=>!!e.offsetParent),m(()=>e.offsetParent.getBoundingClientRect()),m(({x:a})=>a)).subscribe({next(a){a?e.style.setProperty("--md-tooltip-0",`${-a}px`):e.style.removeProperty("--md-tooltip-0")},complete(){e.style.removeProperty("--md-tooltip-0")}}),d(n,"click").pipe(U(s),g(a=>!(a.metaKey||a.ctrlKey))).subscribe(a=>{a.stopPropagation(),a.preventDefault()}),d(n,"mousedown").pipe(U(s),ae(i)).subscribe(([a,{active:c}])=>{var p;if(a.button!==0||a.metaKey||a.ctrlKey)a.preventDefault();else if(c){a.preventDefault();let l=e.parentElement.closest(".md-annotation");l instanceof HTMLElement?l.focus():(p=Re())==null||p.blur()}}),r.pipe(U(s),g(a=>a===o),Ye(125)).subscribe(()=>e.focus()),Sa(e,t).pipe(y(a=>i.next(a)),_(()=>i.complete()),m(a=>F({ref:e},a)))})}function Oa(e){return e.tagName==="CODE"?R(".c, .c1, .cm",e):[e]}function Ma(e){let t=[];for(let r of Oa(e)){let o=[],n=document.createNodeIterator(r,NodeFilter.SHOW_TEXT);for(let i=n.nextNode();i;i=n.nextNode())o.push(i);for(let i of o){let s;for(;s=/(\(\d+\))(!)?/.exec(i.textContent);){let[,a,c]=s;if(typeof c=="undefined"){let p=i.splitText(s.index);i=p.splitText(a.length),t.push(p)}else{i.textContent=a,t.push(i);break}}}}return t}function yn(e,t){t.append(...Array.from(e.childNodes))}function lr(e,t,{target$:r,print$:o}){let n=t.closest("[id]"),i=n==null?void 0:n.id,s=new Map;for(let a of Ma(t)){let[,c]=a.textContent.match(/\((\d+)\)/);me(`:scope > li:nth-child(${c})`,e)&&(s.set(c,un(c,i)),a.replaceWith(s.get(c)))}return s.size===0?L:H(()=>{let a=new v,c=a.pipe(ee(),oe(!0)),p=[];for(let[l,f]of s)p.push([P(".md-typeset",f),P(`:scope > li:nth-child(${l})`,e)]);return o.pipe(U(c)).subscribe(l=>{e.hidden=!l,e.classList.toggle("md-annotation-list",l);for(let[f,u]of p)l?yn(f,u):yn(u,f)}),T(...[...s].map(([,l])=>xn(l,t,{target$:r}))).pipe(_(()=>a.complete()),le())})}function En(e){if(e.nextElementSibling){let t=e.nextElementSibling;if(t.tagName==="OL")return t;if(t.tagName==="P"&&!t.children.length)return En(t)}}function wn(e,t){return H(()=>{let r=En(e);return typeof r!="undefined"?lr(r,e,t):L})}var Tn=jt(zr());var La=0;function Sn(e){if(e.nextElementSibling){let t=e.nextElementSibling;if(t.tagName==="OL")return t;if(t.tagName==="P"&&!t.children.length)return Sn(t)}}function _a(e){return Ee(e).pipe(m(({width:t})=>({scrollable:xt(e).width>t})),X("scrollable"))}function On(e,t){let{matches:r}=matchMedia("(hover)"),o=H(()=>{let n=new v,i=n.pipe($r(1));n.subscribe(({scrollable:c})=>{c&&r?e.setAttribute("tabindex","0"):e.removeAttribute("tabindex")});let s=[];if(Tn.default.isSupported()&&(e.closest(".copy")||G("content.code.copy")&&!e.closest(".no-copy"))){let c=e.closest("pre");c.id=`__code_${La++}`;let p=dn(c.id);c.insertBefore(p,e),G("content.tooltips")&&s.push(Ge(p))}let a=e.closest(".highlight");if(a instanceof HTMLElement){let c=Sn(a);if(typeof c!="undefined"&&(a.classList.contains("annotate")||G("content.code.annotate"))){let p=lr(c,e,t);s.push(Ee(a).pipe(U(i),m(({width:l,height:f})=>l&&f),Y(),b(l=>l?p:L)))}}return _a(e).pipe(y(c=>n.next(c)),_(()=>n.complete()),m(c=>F({ref:e},c)),$e(...s))});return G("content.lazy")?yt(e).pipe(g(n=>n),ye(1),b(()=>o)):o}function Aa(e,{target$:t,print$:r}){let o=!0;return T(t.pipe(m(n=>n.closest("details:not([open])")),g(n=>e===n),m(()=>({action:"open",reveal:!0}))),r.pipe(g(n=>n||!o),y(()=>o=e.open),m(n=>({action:n?"open":"close"}))))}function Mn(e,t){return H(()=>{let r=new v;return r.subscribe(({action:o,reveal:n})=>{e.toggleAttribute("open",o==="open"),n&&e.scrollIntoView()}),Aa(e,t).pipe(y(o=>r.next(o)),_(()=>r.complete()),m(o=>F({ref:e},o)))})}var Ln=".node circle,.node ellipse,.node path,.node polygon,.node rect{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}marker{fill:var(--md-mermaid-edge-color)!important}.edgeLabel .label rect{fill:#0000}.label{color:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.label foreignObject{line-height:normal;overflow:visible}.label div .edgeLabel{color:var(--md-mermaid-label-fg-color)}.edgeLabel,.edgeLabel rect,.label div .edgeLabel{background-color:var(--md-mermaid-label-bg-color)}.edgeLabel,.edgeLabel rect{fill:var(--md-mermaid-label-bg-color);color:var(--md-mermaid-edge-color)}.edgePath .path,.flowchart-link{stroke:var(--md-mermaid-edge-color);stroke-width:.05rem}.edgePath .arrowheadPath{fill:var(--md-mermaid-edge-color);stroke:none}.cluster rect{fill:var(--md-default-fg-color--lightest);stroke:var(--md-default-fg-color--lighter)}.cluster span{color:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}g #flowchart-circleEnd,g #flowchart-circleStart,g #flowchart-crossEnd,g #flowchart-crossStart,g #flowchart-pointEnd,g #flowchart-pointStart{stroke:none}g.classGroup line,g.classGroup rect{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}g.classGroup text{fill:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.classLabel .box{fill:var(--md-mermaid-label-bg-color);background-color:var(--md-mermaid-label-bg-color);opacity:1}.classLabel .label{fill:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.node .divider{stroke:var(--md-mermaid-node-fg-color)}.relation{stroke:var(--md-mermaid-edge-color)}.cardinality{fill:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.cardinality text{fill:inherit!important}defs #classDiagram-compositionEnd,defs #classDiagram-compositionStart,defs #classDiagram-dependencyEnd,defs #classDiagram-dependencyStart,defs #classDiagram-extensionEnd,defs #classDiagram-extensionStart{fill:var(--md-mermaid-edge-color)!important;stroke:var(--md-mermaid-edge-color)!important}defs #classDiagram-aggregationEnd,defs #classDiagram-aggregationStart{fill:var(--md-mermaid-label-bg-color)!important;stroke:var(--md-mermaid-edge-color)!important}g.stateGroup rect{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}g.stateGroup .state-title{fill:var(--md-mermaid-label-fg-color)!important;font-family:var(--md-mermaid-font-family)}g.stateGroup .composit{fill:var(--md-mermaid-label-bg-color)}.nodeLabel,.nodeLabel p{color:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.node circle.state-end,.node circle.state-start,.start-state{fill:var(--md-mermaid-edge-color);stroke:none}.end-state-inner,.end-state-outer{fill:var(--md-mermaid-edge-color)}.end-state-inner,.node circle.state-end{stroke:var(--md-mermaid-label-bg-color)}.transition{stroke:var(--md-mermaid-edge-color)}[id^=state-fork] rect,[id^=state-join] rect{fill:var(--md-mermaid-edge-color)!important;stroke:none!important}.statediagram-cluster.statediagram-cluster .inner{fill:var(--md-default-bg-color)}.statediagram-cluster rect{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}.statediagram-state rect.divider{fill:var(--md-default-fg-color--lightest);stroke:var(--md-default-fg-color--lighter)}defs #statediagram-barbEnd{stroke:var(--md-mermaid-edge-color)}.attributeBoxEven,.attributeBoxOdd{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}.entityBox{fill:var(--md-mermaid-label-bg-color);stroke:var(--md-mermaid-node-fg-color)}.entityLabel{fill:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.relationshipLabelBox{fill:var(--md-mermaid-label-bg-color);fill-opacity:1;background-color:var(--md-mermaid-label-bg-color);opacity:1}.relationshipLabel{fill:var(--md-mermaid-label-fg-color)}.relationshipLine{stroke:var(--md-mermaid-edge-color)}defs #ONE_OR_MORE_END *,defs #ONE_OR_MORE_START *,defs #ONLY_ONE_END *,defs #ONLY_ONE_START *,defs #ZERO_OR_MORE_END *,defs #ZERO_OR_MORE_START *,defs #ZERO_OR_ONE_END *,defs #ZERO_OR_ONE_START *{stroke:var(--md-mermaid-edge-color)!important}defs #ZERO_OR_MORE_END circle,defs #ZERO_OR_MORE_START circle{fill:var(--md-mermaid-label-bg-color)}.actor{fill:var(--md-mermaid-sequence-actor-bg-color);stroke:var(--md-mermaid-sequence-actor-border-color)}text.actor>tspan{fill:var(--md-mermaid-sequence-actor-fg-color);font-family:var(--md-mermaid-font-family)}line{stroke:var(--md-mermaid-sequence-actor-line-color)}.actor-man circle,.actor-man line{fill:var(--md-mermaid-sequence-actorman-bg-color);stroke:var(--md-mermaid-sequence-actorman-line-color)}.messageLine0,.messageLine1{stroke:var(--md-mermaid-sequence-message-line-color)}.note{fill:var(--md-mermaid-sequence-note-bg-color);stroke:var(--md-mermaid-sequence-note-border-color)}.loopText,.loopText>tspan,.messageText,.noteText>tspan{stroke:none;font-family:var(--md-mermaid-font-family)!important}.messageText{fill:var(--md-mermaid-sequence-message-fg-color)}.loopText,.loopText>tspan{fill:var(--md-mermaid-sequence-loop-fg-color)}.noteText>tspan{fill:var(--md-mermaid-sequence-note-fg-color)}#arrowhead path{fill:var(--md-mermaid-sequence-message-line-color);stroke:none}.loopLine{fill:var(--md-mermaid-sequence-loop-bg-color);stroke:var(--md-mermaid-sequence-loop-border-color)}.labelBox{fill:var(--md-mermaid-sequence-label-bg-color);stroke:none}.labelText,.labelText>span{fill:var(--md-mermaid-sequence-label-fg-color);font-family:var(--md-mermaid-font-family)}.sequenceNumber{fill:var(--md-mermaid-sequence-number-fg-color)}rect.rect{fill:var(--md-mermaid-sequence-box-bg-color);stroke:none}rect.rect+text.text{fill:var(--md-mermaid-sequence-box-fg-color)}defs #sequencenumber{fill:var(--md-mermaid-sequence-number-bg-color)!important}";var qr,ka=0;function Ha(){return typeof mermaid=="undefined"||mermaid instanceof Element?gt("https://unpkg.com/mermaid@10.7.0/dist/mermaid.min.js"):$(void 0)}function _n(e){return e.classList.remove("mermaid"),qr||(qr=Ha().pipe(y(()=>mermaid.initialize({startOnLoad:!1,themeCSS:Ln,sequence:{actorFontSize:"16px",messageFontSize:"16px",noteFontSize:"16px"}})),m(()=>{}),B(1))),qr.subscribe(()=>ro(this,null,function*(){e.classList.add("mermaid");let t=`__mermaid_${ka++}`,r=E("div",{class:"mermaid"}),o=e.textContent,{svg:n,fn:i}=yield mermaid.render(t,o),s=r.attachShadow({mode:"closed"});s.innerHTML=n,e.replaceWith(r),i==null||i(s)})),qr.pipe(m(()=>({ref:e})))}var An=E("table");function Cn(e){return e.replaceWith(An),An.replaceWith(vn(e)),$({ref:e})}function $a(e){let t=e.find(r=>r.checked)||e[0];return T(...e.map(r=>d(r,"change").pipe(m(()=>P(`label[for="${r.id}"]`))))).pipe(q(P(`label[for="${t.id}"]`)),m(r=>({active:r})))}function kn(e,{viewport$:t,target$:r}){let o=P(".tabbed-labels",e),n=R(":scope > input",e),i=Nr("prev");e.append(i);let s=Nr("next");return e.append(s),H(()=>{let a=new v,c=a.pipe(ee(),oe(!0));Q([a,Ee(e)]).pipe(U(c),Me(1,de)).subscribe({next([{active:p},l]){let f=Ue(p),{width:u}=pe(p);e.style.setProperty("--md-indicator-x",`${f.x}px`),e.style.setProperty("--md-indicator-width",`${u}px`);let h=ir(o);(f.xh.x+l.width)&&o.scrollTo({left:Math.max(0,f.x-16),behavior:"smooth"})},complete(){e.style.removeProperty("--md-indicator-x"),e.style.removeProperty("--md-indicator-width")}}),Q([et(o),Ee(o)]).pipe(U(c)).subscribe(([p,l])=>{let f=xt(o);i.hidden=p.x<16,s.hidden=p.x>f.width-l.width-16}),T(d(i,"click").pipe(m(()=>-1)),d(s,"click").pipe(m(()=>1))).pipe(U(c)).subscribe(p=>{let{width:l}=pe(o);o.scrollBy({left:l*p,behavior:"smooth"})}),r.pipe(U(c),g(p=>n.includes(p))).subscribe(p=>p.click()),o.classList.add("tabbed-labels--linked");for(let p of n){let l=P(`label[for="${p.id}"]`);l.replaceChildren(E("a",{href:`#${l.htmlFor}`,tabIndex:-1},...Array.from(l.childNodes))),d(l.firstElementChild,"click").pipe(U(c),g(f=>!(f.metaKey||f.ctrlKey)),y(f=>{f.preventDefault(),f.stopPropagation()})).subscribe(()=>{history.replaceState({},"",`#${l.htmlFor}`),l.click()})}return G("content.tabs.link")&&a.pipe(Le(1),ae(t)).subscribe(([{active:p},{offset:l}])=>{let f=p.innerText.trim();if(p.hasAttribute("data-md-switching"))p.removeAttribute("data-md-switching");else{let u=e.offsetTop-l.y;for(let w of R("[data-tabs]"))for(let A of R(":scope > input",w)){let Z=P(`label[for="${A.id}"]`);if(Z!==p&&Z.innerText.trim()===f){Z.setAttribute("data-md-switching",""),A.click();break}}window.scrollTo({top:e.offsetTop-u});let h=__md_get("__tabs")||[];__md_set("__tabs",[...new Set([f,...h])])}}),a.pipe(U(c)).subscribe(()=>{for(let p of R("audio, video",e))p.pause()}),$a(n).pipe(y(p=>a.next(p)),_(()=>a.complete()),m(p=>F({ref:e},p)))}).pipe(ze(ie))}function Hn(e,{viewport$:t,target$:r,print$:o}){return T(...R(".annotate:not(.highlight)",e).map(n=>wn(n,{target$:r,print$:o})),...R("pre:not(.mermaid) > code",e).map(n=>On(n,{target$:r,print$:o})),...R("pre.mermaid",e).map(n=>_n(n)),...R("table:not([class])",e).map(n=>Cn(n)),...R("details",e).map(n=>Mn(n,{target$:r,print$:o})),...R("[data-tabs]",e).map(n=>kn(n,{viewport$:t,target$:r})),...R("[title]",e).filter(()=>G("content.tooltips")).map(n=>Ge(n)))}function Ra(e,{alert$:t}){return t.pipe(b(r=>T($(!0),$(!1).pipe(Ye(2e3))).pipe(m(o=>({message:r,active:o})))))}function $n(e,t){let r=P(".md-typeset",e);return H(()=>{let o=new v;return o.subscribe(({message:n,active:i})=>{e.classList.toggle("md-dialog--active",i),r.textContent=n}),Ra(e,t).pipe(y(n=>o.next(n)),_(()=>o.complete()),m(n=>F({ref:e},n)))})}function Pa({viewport$:e}){if(!G("header.autohide"))return $(!1);let t=e.pipe(m(({offset:{y:n}})=>n),Ke(2,1),m(([n,i])=>[nMath.abs(i-n.y)>100),m(([,[n]])=>n),Y()),o=We("search");return Q([e,o]).pipe(m(([{offset:n},i])=>n.y>400&&!i),Y(),b(n=>n?r:$(!1)),q(!1))}function Rn(e,t){return H(()=>Q([Ee(e),Pa(t)])).pipe(m(([{height:r},o])=>({height:r,hidden:o})),Y((r,o)=>r.height===o.height&&r.hidden===o.hidden),B(1))}function Pn(e,{header$:t,main$:r}){return H(()=>{let o=new v,n=o.pipe(ee(),oe(!0));o.pipe(X("active"),je(t)).subscribe(([{active:s},{hidden:a}])=>{e.classList.toggle("md-header--shadow",s&&!a),e.hidden=a});let i=fe(R("[title]",e)).pipe(g(()=>G("content.tooltips")),re(s=>Ge(s)));return r.subscribe(o),t.pipe(U(n),m(s=>F({ref:e},s)),$e(i.pipe(U(n))))})}function Ia(e,{viewport$:t,header$:r}){return pr(e,{viewport$:t,header$:r}).pipe(m(({offset:{y:o}})=>{let{height:n}=pe(e);return{active:o>=n}}),X("active"))}function In(e,t){return H(()=>{let r=new v;r.subscribe({next({active:n}){e.classList.toggle("md-header__title--active",n)},complete(){e.classList.remove("md-header__title--active")}});let o=me(".md-content h1");return typeof o=="undefined"?L:Ia(o,t).pipe(y(n=>r.next(n)),_(()=>r.complete()),m(n=>F({ref:e},n)))})}function Fn(e,{viewport$:t,header$:r}){let o=r.pipe(m(({height:i})=>i),Y()),n=o.pipe(b(()=>Ee(e).pipe(m(({height:i})=>({top:e.offsetTop,bottom:e.offsetTop+i})),X("bottom"))));return Q([o,n,t]).pipe(m(([i,{top:s,bottom:a},{offset:{y:c},size:{height:p}}])=>(p=Math.max(0,p-Math.max(0,s-c,i)-Math.max(0,p+c-a)),{offset:s-i,height:p,active:s-i<=c})),Y((i,s)=>i.offset===s.offset&&i.height===s.height&&i.active===s.active))}function Fa(e){let t=__md_get("__palette")||{index:e.findIndex(o=>matchMedia(o.getAttribute("data-md-color-media")).matches)},r=Math.max(0,Math.min(t.index,e.length-1));return $(...e).pipe(re(o=>d(o,"change").pipe(m(()=>o))),q(e[r]),m(o=>({index:e.indexOf(o),color:{media:o.getAttribute("data-md-color-media"),scheme:o.getAttribute("data-md-color-scheme"),primary:o.getAttribute("data-md-color-primary"),accent:o.getAttribute("data-md-color-accent")}})),B(1))}function jn(e){let t=R("input",e),r=E("meta",{name:"theme-color"});document.head.appendChild(r);let o=E("meta",{name:"color-scheme"});document.head.appendChild(o);let n=At("(prefers-color-scheme: light)");return H(()=>{let i=new v;return i.subscribe(s=>{if(document.body.setAttribute("data-md-color-switching",""),s.color.media==="(prefers-color-scheme)"){let a=matchMedia("(prefers-color-scheme: light)"),c=document.querySelector(a.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");s.color.scheme=c.getAttribute("data-md-color-scheme"),s.color.primary=c.getAttribute("data-md-color-primary"),s.color.accent=c.getAttribute("data-md-color-accent")}for(let[a,c]of Object.entries(s.color))document.body.setAttribute(`data-md-color-${a}`,c);for(let a=0;a{let s=Te("header"),a=window.getComputedStyle(s);return o.content=a.colorScheme,a.backgroundColor.match(/\d+/g).map(c=>(+c).toString(16).padStart(2,"0")).join("")})).subscribe(s=>r.content=`#${s}`),i.pipe(Oe(ie)).subscribe(()=>{document.body.removeAttribute("data-md-color-switching")}),Fa(t).pipe(U(n.pipe(Le(1))),at(),y(s=>i.next(s)),_(()=>i.complete()),m(s=>F({ref:e},s)))})}function Un(e,{progress$:t}){return H(()=>{let r=new v;return r.subscribe(({value:o})=>{e.style.setProperty("--md-progress-value",`${o}`)}),t.pipe(y(o=>r.next({value:o})),_(()=>r.complete()),m(o=>({ref:e,value:o})))})}var Kr=jt(zr());function ja(e){e.setAttribute("data-md-copying","");let t=e.closest("[data-copy]"),r=t?t.getAttribute("data-copy"):e.innerText;return e.removeAttribute("data-md-copying"),r.trimEnd()}function Wn({alert$:e}){Kr.default.isSupported()&&new j(t=>{new Kr.default("[data-clipboard-target], [data-clipboard-text]",{text:r=>r.getAttribute("data-clipboard-text")||ja(P(r.getAttribute("data-clipboard-target")))}).on("success",r=>t.next(r))}).pipe(y(t=>{t.trigger.focus()}),m(()=>ge("clipboard.copied"))).subscribe(e)}function Dn(e,t){return e.protocol=t.protocol,e.hostname=t.hostname,e}function Ua(e,t){let r=new Map;for(let o of R("url",e)){let n=P("loc",o),i=[Dn(new URL(n.textContent),t)];r.set(`${i[0]}`,i);for(let s of R("[rel=alternate]",o)){let a=s.getAttribute("href");a!=null&&i.push(Dn(new URL(a),t))}}return r}function mr(e){return on(new URL("sitemap.xml",e)).pipe(m(t=>Ua(t,new URL(e))),he(()=>$(new Map)))}function Wa(e,t){if(!(e.target instanceof Element))return L;let r=e.target.closest("a");if(r===null)return L;if(r.target||e.metaKey||e.ctrlKey)return L;let o=new URL(r.href);return o.search=o.hash="",t.has(`${o}`)?(e.preventDefault(),$(new URL(r.href))):L}function Nn(e){let t=new Map;for(let r of R(":scope > *",e.head))t.set(r.outerHTML,r);return t}function Vn(e){for(let t of R("[href], [src]",e))for(let r of["href","src"]){let o=t.getAttribute(r);if(o&&!/^(?:[a-z]+:)?\/\//i.test(o)){t[r]=t[r];break}}return $(e)}function Da(e){for(let o of["[data-md-component=announce]","[data-md-component=container]","[data-md-component=header-topic]","[data-md-component=outdated]","[data-md-component=logo]","[data-md-component=skip]",...G("navigation.tabs.sticky")?["[data-md-component=tabs]"]:[]]){let n=me(o),i=me(o,e);typeof n!="undefined"&&typeof i!="undefined"&&n.replaceWith(i)}let t=Nn(document);for(let[o,n]of Nn(e))t.has(o)?t.delete(o):document.head.appendChild(n);for(let o of t.values())o.remove();let r=Te("container");return Fe(R("script",r)).pipe(b(o=>{let n=e.createElement("script");if(o.src){for(let i of o.getAttributeNames())n.setAttribute(i,o.getAttribute(i));return o.replaceWith(n),new j(i=>{n.onload=()=>i.complete()})}else return n.textContent=o.textContent,o.replaceWith(n),L}),ee(),oe(e))}function zn({location$:e,viewport$:t,progress$:r}){let o=we();if(location.protocol==="file:")return L;let n=mr(o.base);$(document).subscribe(Vn);let i=d(document.body,"click").pipe(je(n),b(([c,p])=>Wa(c,p)),le()),s=d(window,"popstate").pipe(m(ve),le());i.pipe(ae(t)).subscribe(([c,{offset:p}])=>{history.replaceState(p,""),history.pushState(null,"",c)}),T(i,s).subscribe(e);let a=e.pipe(X("pathname"),b(c=>rn(c,{progress$:r}).pipe(he(()=>(st(c,!0),L)))),b(Vn),b(Da),le());return T(a.pipe(ae(e,(c,p)=>p)),e.pipe(X("pathname"),b(()=>e),X("hash")),e.pipe(Y((c,p)=>c.pathname===p.pathname&&c.hash===p.hash),b(()=>i),y(()=>history.back()))).subscribe(c=>{var p,l;history.state!==null||!c.hash?window.scrollTo(0,(l=(p=history.state)==null?void 0:p.y)!=null?l:0):(history.scrollRestoration="auto",Zo(c.hash),history.scrollRestoration="manual")}),e.subscribe(()=>{history.scrollRestoration="manual"}),d(window,"beforeunload").subscribe(()=>{history.scrollRestoration="auto"}),t.pipe(X("offset"),be(100)).subscribe(({offset:c})=>{history.replaceState(c,"")}),a}var Qn=jt(Kn());function Yn(e){let t=e.separator.split("|").map(n=>n.replace(/(\(\?[!=<][^)]+\))/g,"").length===0?"\uFFFD":n).join("|"),r=new RegExp(t,"img"),o=(n,i,s)=>`${i}${s}`;return n=>{n=n.replace(/[\s*+\-:~^]+/g," ").trim();let i=new RegExp(`(^|${e.separator}|)(${n.replace(/[|\\{}()[\]^$+*?.-]/g,"\\$&").replace(r,"|")})`,"img");return s=>(0,Qn.default)(s).replace(i,o).replace(/<\/mark>(\s+)]*>/img,"$1")}}function Ht(e){return e.type===1}function fr(e){return e.type===3}function Bn(e,t){let r=ln(e);return T($(location.protocol!=="file:"),We("search")).pipe(He(o=>o),b(()=>t)).subscribe(({config:o,docs:n})=>r.next({type:0,data:{config:o,docs:n,options:{suggest:G("search.suggest")}}})),r}function Gn({document$:e}){let t=we(),r=De(new URL("../versions.json",t.base)).pipe(he(()=>L)),o=r.pipe(m(n=>{let[,i]=t.base.match(/([^/]+)\/?$/);return n.find(({version:s,aliases:a})=>s===i||a.includes(i))||n[0]}));r.pipe(m(n=>new Map(n.map(i=>[`${new URL(`../${i.version}/`,t.base)}`,i]))),b(n=>d(document.body,"click").pipe(g(i=>!i.metaKey&&!i.ctrlKey),ae(o),b(([i,s])=>{if(i.target instanceof Element){let a=i.target.closest("a");if(a&&!a.target&&n.has(a.href)){let c=a.href;return!i.target.closest(".md-version")&&n.get(c)===s?L:(i.preventDefault(),$(c))}}return L}),b(i=>{let{version:s}=n.get(i);return mr(new URL(i)).pipe(m(a=>{let p=ve().href.replace(t.base,"");return a.has(p.split("#")[0])?new URL(`../${s}/${p}`,t.base):new URL(i)}))})))).subscribe(n=>st(n,!0)),Q([r,o]).subscribe(([n,i])=>{P(".md-header__topic").appendChild(gn(n,i))}),e.pipe(b(()=>o)).subscribe(n=>{var s;let i=__md_get("__outdated",sessionStorage);if(i===null){i=!0;let a=((s=t.version)==null?void 0:s.default)||"latest";Array.isArray(a)||(a=[a]);e:for(let c of a)for(let p of n.aliases.concat(n.version))if(new RegExp(c,"i").test(p)){i=!1;break e}__md_set("__outdated",i,sessionStorage)}if(i)for(let a of ne("outdated"))a.hidden=!1})}function Ka(e,{worker$:t}){let{searchParams:r}=ve();r.has("q")&&(Be("search",!0),e.value=r.get("q"),e.focus(),We("search").pipe(He(i=>!i)).subscribe(()=>{let i=ve();i.searchParams.delete("q"),history.replaceState({},"",`${i}`)}));let o=vt(e),n=T(t.pipe(He(Ht)),d(e,"keyup"),o).pipe(m(()=>e.value),Y());return Q([n,o]).pipe(m(([i,s])=>({value:i,focus:s})),B(1))}function Jn(e,{worker$:t}){let r=new v,o=r.pipe(ee(),oe(!0));Q([t.pipe(He(Ht)),r],(i,s)=>s).pipe(X("value")).subscribe(({value:i})=>t.next({type:2,data:i})),r.pipe(X("focus")).subscribe(({focus:i})=>{i&&Be("search",i)}),d(e.form,"reset").pipe(U(o)).subscribe(()=>e.focus());let n=P("header [for=__search]");return d(n,"click").subscribe(()=>e.focus()),Ka(e,{worker$:t}).pipe(y(i=>r.next(i)),_(()=>r.complete()),m(i=>F({ref:e},i)),B(1))}function Xn(e,{worker$:t,query$:r}){let o=new v,n=Yo(e.parentElement).pipe(g(Boolean)),i=e.parentElement,s=P(":scope > :first-child",e),a=P(":scope > :last-child",e);We("search").subscribe(l=>a.setAttribute("role",l?"list":"presentation")),o.pipe(ae(r),Ir(t.pipe(He(Ht)))).subscribe(([{items:l},{value:f}])=>{switch(l.length){case 0:s.textContent=f.length?ge("search.result.none"):ge("search.result.placeholder");break;case 1:s.textContent=ge("search.result.one");break;default:let u=ar(l.length);s.textContent=ge("search.result.other",u)}});let c=o.pipe(y(()=>a.innerHTML=""),b(({items:l})=>T($(...l.slice(0,10)),$(...l.slice(10)).pipe(Ke(4),jr(n),b(([f])=>f)))),m(hn),le());return c.subscribe(l=>a.appendChild(l)),c.pipe(re(l=>{let f=me("details",l);return typeof f=="undefined"?L:d(f,"toggle").pipe(U(o),m(()=>f))})).subscribe(l=>{l.open===!1&&l.offsetTop<=i.scrollTop&&i.scrollTo({top:l.offsetTop})}),t.pipe(g(fr),m(({data:l})=>l)).pipe(y(l=>o.next(l)),_(()=>o.complete()),m(l=>F({ref:e},l)))}function Qa(e,{query$:t}){return t.pipe(m(({value:r})=>{let o=ve();return o.hash="",r=r.replace(/\s+/g,"+").replace(/&/g,"%26").replace(/=/g,"%3D"),o.search=`q=${r}`,{url:o}}))}function Zn(e,t){let r=new v,o=r.pipe(ee(),oe(!0));return r.subscribe(({url:n})=>{e.setAttribute("data-clipboard-text",e.href),e.href=`${n}`}),d(e,"click").pipe(U(o)).subscribe(n=>n.preventDefault()),Qa(e,t).pipe(y(n=>r.next(n)),_(()=>r.complete()),m(n=>F({ref:e},n)))}function ei(e,{worker$:t,keyboard$:r}){let o=new v,n=Te("search-query"),i=T(d(n,"keydown"),d(n,"focus")).pipe(Oe(ie),m(()=>n.value),Y());return o.pipe(je(i),m(([{suggest:a},c])=>{let p=c.split(/([\s-]+)/);if(a!=null&&a.length&&p[p.length-1]){let l=a[a.length-1];l.startsWith(p[p.length-1])&&(p[p.length-1]=l)}else p.length=0;return p})).subscribe(a=>e.innerHTML=a.join("").replace(/\s/g," ")),r.pipe(g(({mode:a})=>a==="search")).subscribe(a=>{switch(a.type){case"ArrowRight":e.innerText.length&&n.selectionStart===n.value.length&&(n.value=e.innerText);break}}),t.pipe(g(fr),m(({data:a})=>a)).pipe(y(a=>o.next(a)),_(()=>o.complete()),m(()=>({ref:e})))}function ti(e,{index$:t,keyboard$:r}){let o=we();try{let n=Bn(o.search,t),i=Te("search-query",e),s=Te("search-result",e);d(e,"click").pipe(g(({target:c})=>c instanceof Element&&!!c.closest("a"))).subscribe(()=>Be("search",!1)),r.pipe(g(({mode:c})=>c==="search")).subscribe(c=>{let p=Re();switch(c.type){case"Enter":if(p===i){let l=new Map;for(let f of R(":first-child [href]",s)){let u=f.firstElementChild;l.set(f,parseFloat(u.getAttribute("data-md-score")))}if(l.size){let[[f]]=[...l].sort(([,u],[,h])=>h-u);f.click()}c.claim()}break;case"Escape":case"Tab":Be("search",!1),i.blur();break;case"ArrowUp":case"ArrowDown":if(typeof p=="undefined")i.focus();else{let l=[i,...R(":not(details) > [href], summary, details[open] [href]",s)],f=Math.max(0,(Math.max(0,l.indexOf(p))+l.length+(c.type==="ArrowUp"?-1:1))%l.length);l[f].focus()}c.claim();break;default:i!==Re()&&i.focus()}}),r.pipe(g(({mode:c})=>c==="global")).subscribe(c=>{switch(c.type){case"f":case"s":case"/":i.focus(),i.select(),c.claim();break}});let a=Jn(i,{worker$:n});return T(a,Xn(s,{worker$:n,query$:a})).pipe($e(...ne("search-share",e).map(c=>Zn(c,{query$:a})),...ne("search-suggest",e).map(c=>ei(c,{worker$:n,keyboard$:r}))))}catch(n){return e.hidden=!0,qe}}function ri(e,{index$:t,location$:r}){return Q([t,r.pipe(q(ve()),g(o=>!!o.searchParams.get("h")))]).pipe(m(([o,n])=>Yn(o.config)(n.searchParams.get("h"))),m(o=>{var s;let n=new Map,i=document.createNodeIterator(e,NodeFilter.SHOW_TEXT);for(let a=i.nextNode();a;a=i.nextNode())if((s=a.parentElement)!=null&&s.offsetHeight){let c=a.textContent,p=o(c);p.length>c.length&&n.set(a,p)}for(let[a,c]of n){let{childNodes:p}=E("span",null,c);a.replaceWith(...Array.from(p))}return{ref:e,nodes:n}}))}function Ya(e,{viewport$:t,main$:r}){let o=e.closest(".md-grid"),n=o.offsetTop-o.parentElement.offsetTop;return Q([r,t]).pipe(m(([{offset:i,height:s},{offset:{y:a}}])=>(s=s+Math.min(n,Math.max(0,a-i))-n,{height:s,locked:a>=i+n})),Y((i,s)=>i.height===s.height&&i.locked===s.locked))}function Qr(e,o){var n=o,{header$:t}=n,r=to(n,["header$"]);let i=P(".md-sidebar__scrollwrap",e),{y:s}=Ue(i);return H(()=>{let a=new v,c=a.pipe(ee(),oe(!0)),p=a.pipe(Me(0,de));return p.pipe(ae(t)).subscribe({next([{height:l},{height:f}]){i.style.height=`${l-2*s}px`,e.style.top=`${f}px`},complete(){i.style.height="",e.style.top=""}}),p.pipe(He()).subscribe(()=>{for(let l of R(".md-nav__link--active[href]",e)){if(!l.clientHeight)continue;let f=l.closest(".md-sidebar__scrollwrap");if(typeof f!="undefined"){let u=l.offsetTop-f.offsetTop,{height:h}=pe(f);f.scrollTo({top:u-h/2})}}}),fe(R("label[tabindex]",e)).pipe(re(l=>d(l,"click").pipe(Oe(ie),m(()=>l),U(c)))).subscribe(l=>{let f=P(`[id="${l.htmlFor}"]`);P(`[aria-labelledby="${l.id}"]`).setAttribute("aria-expanded",`${f.checked}`)}),Ya(e,r).pipe(y(l=>a.next(l)),_(()=>a.complete()),m(l=>F({ref:e},l)))})}function oi(e,t){if(typeof t!="undefined"){let r=`https://api.github.com/repos/${e}/${t}`;return Lt(De(`${r}/releases/latest`).pipe(he(()=>L),m(o=>({version:o.tag_name})),Qe({})),De(r).pipe(he(()=>L),m(o=>({stars:o.stargazers_count,forks:o.forks_count})),Qe({}))).pipe(m(([o,n])=>F(F({},o),n)))}else{let r=`https://api.github.com/users/${e}`;return De(r).pipe(m(o=>({repositories:o.public_repos})),Qe({}))}}function ni(e,t){let r=`https://${e}/api/v4/projects/${encodeURIComponent(t)}`;return De(r).pipe(he(()=>L),m(({star_count:o,forks_count:n})=>({stars:o,forks:n})),Qe({}))}function ii(e){let t=e.match(/^.+github\.com\/([^/]+)\/?([^/]+)?/i);if(t){let[,r,o]=t;return oi(r,o)}if(t=e.match(/^.+?([^/]*gitlab[^/]+)\/(.+?)\/?$/i),t){let[,r,o]=t;return ni(r,o)}return L}var Ba;function Ga(e){return Ba||(Ba=H(()=>{let t=__md_get("__source",sessionStorage);if(t)return $(t);if(ne("consent").length){let o=__md_get("__consent");if(!(o&&o.github))return L}return ii(e.href).pipe(y(o=>__md_set("__source",o,sessionStorage)))}).pipe(he(()=>L),g(t=>Object.keys(t).length>0),m(t=>({facts:t})),B(1)))}function ai(e){let t=P(":scope > :last-child",e);return H(()=>{let r=new v;return r.subscribe(({facts:o})=>{t.appendChild(bn(o)),t.classList.add("md-source__repository--active")}),Ga(e).pipe(y(o=>r.next(o)),_(()=>r.complete()),m(o=>F({ref:e},o)))})}function Ja(e,{viewport$:t,header$:r}){return Ee(document.body).pipe(b(()=>pr(e,{header$:r,viewport$:t})),m(({offset:{y:o}})=>({hidden:o>=10})),X("hidden"))}function si(e,t){return H(()=>{let r=new v;return r.subscribe({next({hidden:o}){e.hidden=o},complete(){e.hidden=!1}}),(G("navigation.tabs.sticky")?$({hidden:!1}):Ja(e,t)).pipe(y(o=>r.next(o)),_(()=>r.complete()),m(o=>F({ref:e},o)))})}function Xa(e,{viewport$:t,header$:r}){let o=new Map,n=R(".md-nav__link",e);for(let a of n){let c=decodeURIComponent(a.hash.substring(1)),p=me(`[id="${c}"]`);typeof p!="undefined"&&o.set(a,p)}let i=r.pipe(X("height"),m(({height:a})=>{let c=Te("main"),p=P(":scope > :first-child",c);return a+.8*(p.offsetTop-c.offsetTop)}),le());return Ee(document.body).pipe(X("height"),b(a=>H(()=>{let c=[];return $([...o].reduce((p,[l,f])=>{for(;c.length&&o.get(c[c.length-1]).tagName>=f.tagName;)c.pop();let u=f.offsetTop;for(;!u&&f.parentElement;)f=f.parentElement,u=f.offsetTop;let h=f.offsetParent;for(;h;h=h.offsetParent)u+=h.offsetTop;return p.set([...c=[...c,l]].reverse(),u)},new Map))}).pipe(m(c=>new Map([...c].sort(([,p],[,l])=>p-l))),je(i),b(([c,p])=>t.pipe(Rr(([l,f],{offset:{y:u},size:h})=>{let w=u+h.height>=Math.floor(a.height);for(;f.length;){let[,A]=f[0];if(A-p=u&&!w)f=[l.pop(),...f];else break}return[l,f]},[[],[...c]]),Y((l,f)=>l[0]===f[0]&&l[1]===f[1])))))).pipe(m(([a,c])=>({prev:a.map(([p])=>p),next:c.map(([p])=>p)})),q({prev:[],next:[]}),Ke(2,1),m(([a,c])=>a.prev.length{let i=new v,s=i.pipe(ee(),oe(!0));if(i.subscribe(({prev:a,next:c})=>{for(let[p]of c)p.classList.remove("md-nav__link--passed"),p.classList.remove("md-nav__link--active");for(let[p,[l]]of a.entries())l.classList.add("md-nav__link--passed"),l.classList.toggle("md-nav__link--active",p===a.length-1)}),G("toc.follow")){let a=T(t.pipe(be(1),m(()=>{})),t.pipe(be(250),m(()=>"smooth")));i.pipe(g(({prev:c})=>c.length>0),je(o.pipe(Oe(ie))),ae(a)).subscribe(([[{prev:c}],p])=>{let[l]=c[c.length-1];if(l.offsetHeight){let f=sr(l);if(typeof f!="undefined"){let u=l.offsetTop-f.offsetTop,{height:h}=pe(f);f.scrollTo({top:u-h/2,behavior:p})}}})}return G("navigation.tracking")&&t.pipe(U(s),X("offset"),be(250),Le(1),U(n.pipe(Le(1))),at({delay:250}),ae(i)).subscribe(([,{prev:a}])=>{let c=ve(),p=a[a.length-1];if(p&&p.length){let[l]=p,{hash:f}=new URL(l.href);c.hash!==f&&(c.hash=f,history.replaceState({},"",`${c}`))}else c.hash="",history.replaceState({},"",`${c}`)}),Xa(e,{viewport$:t,header$:r}).pipe(y(a=>i.next(a)),_(()=>i.complete()),m(a=>F({ref:e},a)))})}function Za(e,{viewport$:t,main$:r,target$:o}){let n=t.pipe(m(({offset:{y:s}})=>s),Ke(2,1),m(([s,a])=>s>a&&a>0),Y()),i=r.pipe(m(({active:s})=>s));return Q([i,n]).pipe(m(([s,a])=>!(s&&a)),Y(),U(o.pipe(Le(1))),oe(!0),at({delay:250}),m(s=>({hidden:s})))}function pi(e,{viewport$:t,header$:r,main$:o,target$:n}){let i=new v,s=i.pipe(ee(),oe(!0));return i.subscribe({next({hidden:a}){e.hidden=a,a?(e.setAttribute("tabindex","-1"),e.blur()):e.removeAttribute("tabindex")},complete(){e.style.top="",e.hidden=!0,e.removeAttribute("tabindex")}}),r.pipe(U(s),X("height")).subscribe(({height:a})=>{e.style.top=`${a+16}px`}),d(e,"click").subscribe(a=>{a.preventDefault(),window.scrollTo({top:0})}),Za(e,{viewport$:t,main$:o,target$:n}).pipe(y(a=>i.next(a)),_(()=>i.complete()),m(a=>F({ref:e},a)))}function li({document$:e}){e.pipe(b(()=>R(".md-ellipsis")),re(t=>yt(t).pipe(U(e.pipe(Le(1))),g(r=>r),m(()=>t),ye(1))),g(t=>t.offsetWidth{let r=t.innerText,o=t.closest("a")||t;return o.title=r,Ge(o).pipe(U(e.pipe(Le(1))),_(()=>o.removeAttribute("title")))})).subscribe(),e.pipe(b(()=>R(".md-status")),re(t=>Ge(t))).subscribe()}function mi({document$:e,tablet$:t}){e.pipe(b(()=>R(".md-toggle--indeterminate")),y(r=>{r.indeterminate=!0,r.checked=!1}),re(r=>d(r,"change").pipe(Fr(()=>r.classList.contains("md-toggle--indeterminate")),m(()=>r))),ae(t)).subscribe(([r,o])=>{r.classList.remove("md-toggle--indeterminate"),o&&(r.checked=!1)})}function es(){return/(iPad|iPhone|iPod)/.test(navigator.userAgent)}function fi({document$:e}){e.pipe(b(()=>R("[data-md-scrollfix]")),y(t=>t.removeAttribute("data-md-scrollfix")),g(es),re(t=>d(t,"touchstart").pipe(m(()=>t)))).subscribe(t=>{let r=t.scrollTop;r===0?t.scrollTop=1:r+t.offsetHeight===t.scrollHeight&&(t.scrollTop=r-1)})}function ui({viewport$:e,tablet$:t}){Q([We("search"),t]).pipe(m(([r,o])=>r&&!o),b(r=>$(r).pipe(Ye(r?400:100))),ae(e)).subscribe(([r,{offset:{y:o}}])=>{if(r)document.body.setAttribute("data-md-scrolllock",""),document.body.style.top=`-${o}px`;else{let n=-1*parseInt(document.body.style.top,10);document.body.removeAttribute("data-md-scrolllock"),document.body.style.top="",n&&window.scrollTo(0,n)}})}Object.entries||(Object.entries=function(e){let t=[];for(let r of Object.keys(e))t.push([r,e[r]]);return t});Object.values||(Object.values=function(e){let t=[];for(let r of Object.keys(e))t.push(e[r]);return t});typeof Element!="undefined"&&(Element.prototype.scrollTo||(Element.prototype.scrollTo=function(e,t){typeof e=="object"?(this.scrollLeft=e.left,this.scrollTop=e.top):(this.scrollLeft=e,this.scrollTop=t)}),Element.prototype.replaceWith||(Element.prototype.replaceWith=function(...e){let t=this.parentNode;if(t){e.length===0&&t.removeChild(this);for(let r=e.length-1;r>=0;r--){let o=e[r];typeof o=="string"?o=document.createTextNode(o):o.parentNode&&o.parentNode.removeChild(o),r?t.insertBefore(this.previousSibling,o):t.replaceChild(o,this)}}}));function ts(){return location.protocol==="file:"?gt(`${new URL("search/search_index.js",Yr.base)}`).pipe(m(()=>__index),B(1)):De(new URL("search/search_index.json",Yr.base))}document.documentElement.classList.remove("no-js");document.documentElement.classList.add("js");var rt=No(),Rt=Jo(),wt=en(Rt),Br=Go(),_e=pn(),ur=At("(min-width: 960px)"),hi=At("(min-width: 1220px)"),bi=tn(),Yr=we(),vi=document.forms.namedItem("search")?ts():qe,Gr=new v;Wn({alert$:Gr});var Jr=new v;G("navigation.instant")&&zn({location$:Rt,viewport$:_e,progress$:Jr}).subscribe(rt);var di;((di=Yr.version)==null?void 0:di.provider)==="mike"&&Gn({document$:rt});T(Rt,wt).pipe(Ye(125)).subscribe(()=>{Be("drawer",!1),Be("search",!1)});Br.pipe(g(({mode:e})=>e==="global")).subscribe(e=>{switch(e.type){case"p":case",":let t=me("link[rel=prev]");typeof t!="undefined"&&st(t);break;case"n":case".":let r=me("link[rel=next]");typeof r!="undefined"&&st(r);break;case"Enter":let o=Re();o instanceof HTMLLabelElement&&o.click()}});li({document$:rt});mi({document$:rt,tablet$:ur});fi({document$:rt});ui({viewport$:_e,tablet$:ur});var tt=Rn(Te("header"),{viewport$:_e}),$t=rt.pipe(m(()=>Te("main")),b(e=>Fn(e,{viewport$:_e,header$:tt})),B(1)),rs=T(...ne("consent").map(e=>fn(e,{target$:wt})),...ne("dialog").map(e=>$n(e,{alert$:Gr})),...ne("header").map(e=>Pn(e,{viewport$:_e,header$:tt,main$:$t})),...ne("palette").map(e=>jn(e)),...ne("progress").map(e=>Un(e,{progress$:Jr})),...ne("search").map(e=>ti(e,{index$:vi,keyboard$:Br})),...ne("source").map(e=>ai(e))),os=H(()=>T(...ne("announce").map(e=>mn(e)),...ne("content").map(e=>Hn(e,{viewport$:_e,target$:wt,print$:bi})),...ne("content").map(e=>G("search.highlight")?ri(e,{index$:vi,location$:Rt}):L),...ne("header-title").map(e=>In(e,{viewport$:_e,header$:tt})),...ne("sidebar").map(e=>e.getAttribute("data-md-type")==="navigation"?Ur(hi,()=>Qr(e,{viewport$:_e,header$:tt,main$:$t})):Ur(ur,()=>Qr(e,{viewport$:_e,header$:tt,main$:$t}))),...ne("tabs").map(e=>si(e,{viewport$:_e,header$:tt})),...ne("toc").map(e=>ci(e,{viewport$:_e,header$:tt,main$:$t,target$:wt})),...ne("top").map(e=>pi(e,{viewport$:_e,header$:tt,main$:$t,target$:wt})))),gi=rt.pipe(b(()=>os),$e(rs),B(1));gi.subscribe();window.document$=rt;window.location$=Rt;window.target$=wt;window.keyboard$=Br;window.viewport$=_e;window.tablet$=ur;window.screen$=hi;window.print$=bi;window.alert$=Gr;window.progress$=Jr;window.component$=gi;})(); -//# sourceMappingURL=bundle.8fd75fb4.min.js.map - diff --git a/assets/javascripts/bundle.8fd75fb4.min.js.map b/assets/javascripts/bundle.8fd75fb4.min.js.map deleted file mode 100644 index 1a287892a..000000000 --- a/assets/javascripts/bundle.8fd75fb4.min.js.map +++ /dev/null @@ -1,7 +0,0 @@ -{ - "version": 3, - "sources": ["node_modules/focus-visible/dist/focus-visible.js", "node_modules/clipboard/dist/clipboard.js", "node_modules/escape-html/index.js", "src/templates/assets/javascripts/bundle.ts", "node_modules/rxjs/node_modules/tslib/tslib.es6.js", "node_modules/rxjs/src/internal/util/isFunction.ts", "node_modules/rxjs/src/internal/util/createErrorClass.ts", "node_modules/rxjs/src/internal/util/UnsubscriptionError.ts", "node_modules/rxjs/src/internal/util/arrRemove.ts", "node_modules/rxjs/src/internal/Subscription.ts", "node_modules/rxjs/src/internal/config.ts", "node_modules/rxjs/src/internal/scheduler/timeoutProvider.ts", "node_modules/rxjs/src/internal/util/reportUnhandledError.ts", "node_modules/rxjs/src/internal/util/noop.ts", "node_modules/rxjs/src/internal/NotificationFactories.ts", "node_modules/rxjs/src/internal/util/errorContext.ts", "node_modules/rxjs/src/internal/Subscriber.ts", "node_modules/rxjs/src/internal/symbol/observable.ts", "node_modules/rxjs/src/internal/util/identity.ts", "node_modules/rxjs/src/internal/util/pipe.ts", "node_modules/rxjs/src/internal/Observable.ts", "node_modules/rxjs/src/internal/util/lift.ts", "node_modules/rxjs/src/internal/operators/OperatorSubscriber.ts", "node_modules/rxjs/src/internal/scheduler/animationFrameProvider.ts", "node_modules/rxjs/src/internal/util/ObjectUnsubscribedError.ts", "node_modules/rxjs/src/internal/Subject.ts", "node_modules/rxjs/src/internal/scheduler/dateTimestampProvider.ts", "node_modules/rxjs/src/internal/ReplaySubject.ts", "node_modules/rxjs/src/internal/scheduler/Action.ts", "node_modules/rxjs/src/internal/scheduler/intervalProvider.ts", "node_modules/rxjs/src/internal/scheduler/AsyncAction.ts", "node_modules/rxjs/src/internal/Scheduler.ts", "node_modules/rxjs/src/internal/scheduler/AsyncScheduler.ts", "node_modules/rxjs/src/internal/scheduler/async.ts", "node_modules/rxjs/src/internal/scheduler/AnimationFrameAction.ts", "node_modules/rxjs/src/internal/scheduler/AnimationFrameScheduler.ts", "node_modules/rxjs/src/internal/scheduler/animationFrame.ts", "node_modules/rxjs/src/internal/observable/empty.ts", "node_modules/rxjs/src/internal/util/isScheduler.ts", "node_modules/rxjs/src/internal/util/args.ts", "node_modules/rxjs/src/internal/util/isArrayLike.ts", "node_modules/rxjs/src/internal/util/isPromise.ts", "node_modules/rxjs/src/internal/util/isInteropObservable.ts", "node_modules/rxjs/src/internal/util/isAsyncIterable.ts", "node_modules/rxjs/src/internal/util/throwUnobservableError.ts", "node_modules/rxjs/src/internal/symbol/iterator.ts", "node_modules/rxjs/src/internal/util/isIterable.ts", "node_modules/rxjs/src/internal/util/isReadableStreamLike.ts", "node_modules/rxjs/src/internal/observable/innerFrom.ts", "node_modules/rxjs/src/internal/util/executeSchedule.ts", "node_modules/rxjs/src/internal/operators/observeOn.ts", "node_modules/rxjs/src/internal/operators/subscribeOn.ts", "node_modules/rxjs/src/internal/scheduled/scheduleObservable.ts", "node_modules/rxjs/src/internal/scheduled/schedulePromise.ts", "node_modules/rxjs/src/internal/scheduled/scheduleArray.ts", "node_modules/rxjs/src/internal/scheduled/scheduleIterable.ts", "node_modules/rxjs/src/internal/scheduled/scheduleAsyncIterable.ts", "node_modules/rxjs/src/internal/scheduled/scheduleReadableStreamLike.ts", "node_modules/rxjs/src/internal/scheduled/scheduled.ts", "node_modules/rxjs/src/internal/observable/from.ts", "node_modules/rxjs/src/internal/observable/of.ts", "node_modules/rxjs/src/internal/observable/throwError.ts", "node_modules/rxjs/src/internal/util/EmptyError.ts", "node_modules/rxjs/src/internal/util/isDate.ts", "node_modules/rxjs/src/internal/operators/map.ts", "node_modules/rxjs/src/internal/util/mapOneOrManyArgs.ts", "node_modules/rxjs/src/internal/util/argsArgArrayOrObject.ts", "node_modules/rxjs/src/internal/util/createObject.ts", "node_modules/rxjs/src/internal/observable/combineLatest.ts", "node_modules/rxjs/src/internal/operators/mergeInternals.ts", "node_modules/rxjs/src/internal/operators/mergeMap.ts", "node_modules/rxjs/src/internal/operators/mergeAll.ts", "node_modules/rxjs/src/internal/operators/concatAll.ts", "node_modules/rxjs/src/internal/observable/concat.ts", "node_modules/rxjs/src/internal/observable/defer.ts", "node_modules/rxjs/src/internal/observable/fromEvent.ts", "node_modules/rxjs/src/internal/observable/fromEventPattern.ts", "node_modules/rxjs/src/internal/observable/timer.ts", "node_modules/rxjs/src/internal/observable/merge.ts", "node_modules/rxjs/src/internal/observable/never.ts", "node_modules/rxjs/src/internal/util/argsOrArgArray.ts", "node_modules/rxjs/src/internal/operators/filter.ts", "node_modules/rxjs/src/internal/observable/zip.ts", "node_modules/rxjs/src/internal/operators/audit.ts", "node_modules/rxjs/src/internal/operators/auditTime.ts", "node_modules/rxjs/src/internal/operators/bufferCount.ts", "node_modules/rxjs/src/internal/operators/catchError.ts", "node_modules/rxjs/src/internal/operators/scanInternals.ts", "node_modules/rxjs/src/internal/operators/combineLatest.ts", "node_modules/rxjs/src/internal/operators/combineLatestWith.ts", "node_modules/rxjs/src/internal/operators/debounceTime.ts", "node_modules/rxjs/src/internal/operators/defaultIfEmpty.ts", "node_modules/rxjs/src/internal/operators/take.ts", "node_modules/rxjs/src/internal/operators/ignoreElements.ts", "node_modules/rxjs/src/internal/operators/mapTo.ts", "node_modules/rxjs/src/internal/operators/delayWhen.ts", "node_modules/rxjs/src/internal/operators/delay.ts", "node_modules/rxjs/src/internal/operators/distinctUntilChanged.ts", "node_modules/rxjs/src/internal/operators/distinctUntilKeyChanged.ts", "node_modules/rxjs/src/internal/operators/throwIfEmpty.ts", "node_modules/rxjs/src/internal/operators/endWith.ts", "node_modules/rxjs/src/internal/operators/finalize.ts", "node_modules/rxjs/src/internal/operators/first.ts", "node_modules/rxjs/src/internal/operators/takeLast.ts", "node_modules/rxjs/src/internal/operators/merge.ts", "node_modules/rxjs/src/internal/operators/mergeWith.ts", "node_modules/rxjs/src/internal/operators/repeat.ts", "node_modules/rxjs/src/internal/operators/scan.ts", "node_modules/rxjs/src/internal/operators/share.ts", "node_modules/rxjs/src/internal/operators/shareReplay.ts", "node_modules/rxjs/src/internal/operators/skip.ts", "node_modules/rxjs/src/internal/operators/skipUntil.ts", "node_modules/rxjs/src/internal/operators/startWith.ts", "node_modules/rxjs/src/internal/operators/switchMap.ts", "node_modules/rxjs/src/internal/operators/takeUntil.ts", "node_modules/rxjs/src/internal/operators/takeWhile.ts", "node_modules/rxjs/src/internal/operators/tap.ts", "node_modules/rxjs/src/internal/operators/throttle.ts", "node_modules/rxjs/src/internal/operators/throttleTime.ts", "node_modules/rxjs/src/internal/operators/withLatestFrom.ts", "node_modules/rxjs/src/internal/operators/zip.ts", "node_modules/rxjs/src/internal/operators/zipWith.ts", "src/templates/assets/javascripts/browser/document/index.ts", "src/templates/assets/javascripts/browser/element/_/index.ts", "src/templates/assets/javascripts/browser/element/focus/index.ts", "src/templates/assets/javascripts/browser/element/hover/index.ts", "src/templates/assets/javascripts/browser/element/offset/_/index.ts", "src/templates/assets/javascripts/browser/element/offset/content/index.ts", "src/templates/assets/javascripts/utilities/h/index.ts", "src/templates/assets/javascripts/utilities/round/index.ts", "src/templates/assets/javascripts/browser/script/index.ts", "src/templates/assets/javascripts/browser/element/size/_/index.ts", "src/templates/assets/javascripts/browser/element/size/content/index.ts", "src/templates/assets/javascripts/browser/element/visibility/index.ts", "src/templates/assets/javascripts/browser/toggle/index.ts", "src/templates/assets/javascripts/browser/keyboard/index.ts", "src/templates/assets/javascripts/browser/location/_/index.ts", "src/templates/assets/javascripts/browser/location/hash/index.ts", "src/templates/assets/javascripts/browser/media/index.ts", "src/templates/assets/javascripts/browser/request/index.ts", "src/templates/assets/javascripts/browser/viewport/offset/index.ts", "src/templates/assets/javascripts/browser/viewport/size/index.ts", "src/templates/assets/javascripts/browser/viewport/_/index.ts", "src/templates/assets/javascripts/browser/viewport/at/index.ts", "src/templates/assets/javascripts/browser/worker/index.ts", "src/templates/assets/javascripts/_/index.ts", "src/templates/assets/javascripts/components/_/index.ts", "src/templates/assets/javascripts/components/announce/index.ts", "src/templates/assets/javascripts/components/consent/index.ts", "src/templates/assets/javascripts/templates/tooltip/index.tsx", "src/templates/assets/javascripts/templates/annotation/index.tsx", "src/templates/assets/javascripts/templates/clipboard/index.tsx", "src/templates/assets/javascripts/templates/search/index.tsx", "src/templates/assets/javascripts/templates/source/index.tsx", "src/templates/assets/javascripts/templates/tabbed/index.tsx", "src/templates/assets/javascripts/templates/table/index.tsx", "src/templates/assets/javascripts/templates/version/index.tsx", "src/templates/assets/javascripts/components/tooltip/index.ts", "src/templates/assets/javascripts/components/content/annotation/_/index.ts", "src/templates/assets/javascripts/components/content/annotation/list/index.ts", "src/templates/assets/javascripts/components/content/annotation/block/index.ts", "src/templates/assets/javascripts/components/content/code/_/index.ts", "src/templates/assets/javascripts/components/content/details/index.ts", "src/templates/assets/javascripts/components/content/mermaid/index.css", "src/templates/assets/javascripts/components/content/mermaid/index.ts", "src/templates/assets/javascripts/components/content/table/index.ts", "src/templates/assets/javascripts/components/content/tabs/index.ts", "src/templates/assets/javascripts/components/content/_/index.ts", "src/templates/assets/javascripts/components/dialog/index.ts", "src/templates/assets/javascripts/components/header/_/index.ts", "src/templates/assets/javascripts/components/header/title/index.ts", "src/templates/assets/javascripts/components/main/index.ts", "src/templates/assets/javascripts/components/palette/index.ts", "src/templates/assets/javascripts/components/progress/index.ts", "src/templates/assets/javascripts/integrations/clipboard/index.ts", "src/templates/assets/javascripts/integrations/sitemap/index.ts", "src/templates/assets/javascripts/integrations/instant/index.ts", "src/templates/assets/javascripts/integrations/search/highlighter/index.ts", "src/templates/assets/javascripts/integrations/search/worker/message/index.ts", "src/templates/assets/javascripts/integrations/search/worker/_/index.ts", "src/templates/assets/javascripts/integrations/version/index.ts", "src/templates/assets/javascripts/components/search/query/index.ts", "src/templates/assets/javascripts/components/search/result/index.ts", "src/templates/assets/javascripts/components/search/share/index.ts", "src/templates/assets/javascripts/components/search/suggest/index.ts", "src/templates/assets/javascripts/components/search/_/index.ts", "src/templates/assets/javascripts/components/search/highlight/index.ts", "src/templates/assets/javascripts/components/sidebar/index.ts", "src/templates/assets/javascripts/components/source/facts/github/index.ts", "src/templates/assets/javascripts/components/source/facts/gitlab/index.ts", "src/templates/assets/javascripts/components/source/facts/_/index.ts", "src/templates/assets/javascripts/components/source/_/index.ts", "src/templates/assets/javascripts/components/tabs/index.ts", "src/templates/assets/javascripts/components/toc/index.ts", "src/templates/assets/javascripts/components/top/index.ts", "src/templates/assets/javascripts/patches/ellipsis/index.ts", "src/templates/assets/javascripts/patches/indeterminate/index.ts", "src/templates/assets/javascripts/patches/scrollfix/index.ts", "src/templates/assets/javascripts/patches/scrolllock/index.ts", "src/templates/assets/javascripts/polyfills/index.ts"], - "sourcesContent": ["(function (global, factory) {\n typeof exports === 'object' && typeof module !== 'undefined' ? factory() :\n typeof define === 'function' && define.amd ? define(factory) :\n (factory());\n}(this, (function () { 'use strict';\n\n /**\n * Applies the :focus-visible polyfill at the given scope.\n * A scope in this case is either the top-level Document or a Shadow Root.\n *\n * @param {(Document|ShadowRoot)} scope\n * @see https://github.com/WICG/focus-visible\n */\n function applyFocusVisiblePolyfill(scope) {\n var hadKeyboardEvent = true;\n var hadFocusVisibleRecently = false;\n var hadFocusVisibleRecentlyTimeout = null;\n\n var inputTypesAllowlist = {\n text: true,\n search: true,\n url: true,\n tel: true,\n email: true,\n password: true,\n number: true,\n date: true,\n month: true,\n week: true,\n time: true,\n datetime: true,\n 'datetime-local': true\n };\n\n /**\n * Helper function for legacy browsers and iframes which sometimes focus\n * elements like document, body, and non-interactive SVG.\n * @param {Element} el\n */\n function isValidFocusTarget(el) {\n if (\n el &&\n el !== document &&\n el.nodeName !== 'HTML' &&\n el.nodeName !== 'BODY' &&\n 'classList' in el &&\n 'contains' in el.classList\n ) {\n return true;\n }\n return false;\n }\n\n /**\n * Computes whether the given element should automatically trigger the\n * `focus-visible` class being added, i.e. whether it should always match\n * `:focus-visible` when focused.\n * @param {Element} el\n * @return {boolean}\n */\n function focusTriggersKeyboardModality(el) {\n var type = el.type;\n var tagName = el.tagName;\n\n if (tagName === 'INPUT' && inputTypesAllowlist[type] && !el.readOnly) {\n return true;\n }\n\n if (tagName === 'TEXTAREA' && !el.readOnly) {\n return true;\n }\n\n if (el.isContentEditable) {\n return true;\n }\n\n return false;\n }\n\n /**\n * Add the `focus-visible` class to the given element if it was not added by\n * the author.\n * @param {Element} el\n */\n function addFocusVisibleClass(el) {\n if (el.classList.contains('focus-visible')) {\n return;\n }\n el.classList.add('focus-visible');\n el.setAttribute('data-focus-visible-added', '');\n }\n\n /**\n * Remove the `focus-visible` class from the given element if it was not\n * originally added by the author.\n * @param {Element} el\n */\n function removeFocusVisibleClass(el) {\n if (!el.hasAttribute('data-focus-visible-added')) {\n return;\n }\n el.classList.remove('focus-visible');\n el.removeAttribute('data-focus-visible-added');\n }\n\n /**\n * If the most recent user interaction was via the keyboard;\n * and the key press did not include a meta, alt/option, or control key;\n * then the modality is keyboard. Otherwise, the modality is not keyboard.\n * Apply `focus-visible` to any current active element and keep track\n * of our keyboard modality state with `hadKeyboardEvent`.\n * @param {KeyboardEvent} e\n */\n function onKeyDown(e) {\n if (e.metaKey || e.altKey || e.ctrlKey) {\n return;\n }\n\n if (isValidFocusTarget(scope.activeElement)) {\n addFocusVisibleClass(scope.activeElement);\n }\n\n hadKeyboardEvent = true;\n }\n\n /**\n * If at any point a user clicks with a pointing device, ensure that we change\n * the modality away from keyboard.\n * This avoids the situation where a user presses a key on an already focused\n * element, and then clicks on a different element, focusing it with a\n * pointing device, while we still think we're in keyboard modality.\n * @param {Event} e\n */\n function onPointerDown(e) {\n hadKeyboardEvent = false;\n }\n\n /**\n * On `focus`, add the `focus-visible` class to the target if:\n * - the target received focus as a result of keyboard navigation, or\n * - the event target is an element that will likely require interaction\n * via the keyboard (e.g. a text box)\n * @param {Event} e\n */\n function onFocus(e) {\n // Prevent IE from focusing the document or HTML element.\n if (!isValidFocusTarget(e.target)) {\n return;\n }\n\n if (hadKeyboardEvent || focusTriggersKeyboardModality(e.target)) {\n addFocusVisibleClass(e.target);\n }\n }\n\n /**\n * On `blur`, remove the `focus-visible` class from the target.\n * @param {Event} e\n */\n function onBlur(e) {\n if (!isValidFocusTarget(e.target)) {\n return;\n }\n\n if (\n e.target.classList.contains('focus-visible') ||\n e.target.hasAttribute('data-focus-visible-added')\n ) {\n // To detect a tab/window switch, we look for a blur event followed\n // rapidly by a visibility change.\n // If we don't see a visibility change within 100ms, it's probably a\n // regular focus change.\n hadFocusVisibleRecently = true;\n window.clearTimeout(hadFocusVisibleRecentlyTimeout);\n hadFocusVisibleRecentlyTimeout = window.setTimeout(function() {\n hadFocusVisibleRecently = false;\n }, 100);\n removeFocusVisibleClass(e.target);\n }\n }\n\n /**\n * If the user changes tabs, keep track of whether or not the previously\n * focused element had .focus-visible.\n * @param {Event} e\n */\n function onVisibilityChange(e) {\n if (document.visibilityState === 'hidden') {\n // If the tab becomes active again, the browser will handle calling focus\n // on the element (Safari actually calls it twice).\n // If this tab change caused a blur on an element with focus-visible,\n // re-apply the class when the user switches back to the tab.\n if (hadFocusVisibleRecently) {\n hadKeyboardEvent = true;\n }\n addInitialPointerMoveListeners();\n }\n }\n\n /**\n * Add a group of listeners to detect usage of any pointing devices.\n * These listeners will be added when the polyfill first loads, and anytime\n * the window is blurred, so that they are active when the window regains\n * focus.\n */\n function addInitialPointerMoveListeners() {\n document.addEventListener('mousemove', onInitialPointerMove);\n document.addEventListener('mousedown', onInitialPointerMove);\n document.addEventListener('mouseup', onInitialPointerMove);\n document.addEventListener('pointermove', onInitialPointerMove);\n document.addEventListener('pointerdown', onInitialPointerMove);\n document.addEventListener('pointerup', onInitialPointerMove);\n document.addEventListener('touchmove', onInitialPointerMove);\n document.addEventListener('touchstart', onInitialPointerMove);\n document.addEventListener('touchend', onInitialPointerMove);\n }\n\n function removeInitialPointerMoveListeners() {\n document.removeEventListener('mousemove', onInitialPointerMove);\n document.removeEventListener('mousedown', onInitialPointerMove);\n document.removeEventListener('mouseup', onInitialPointerMove);\n document.removeEventListener('pointermove', onInitialPointerMove);\n document.removeEventListener('pointerdown', onInitialPointerMove);\n document.removeEventListener('pointerup', onInitialPointerMove);\n document.removeEventListener('touchmove', onInitialPointerMove);\n document.removeEventListener('touchstart', onInitialPointerMove);\n document.removeEventListener('touchend', onInitialPointerMove);\n }\n\n /**\n * When the polfyill first loads, assume the user is in keyboard modality.\n * If any event is received from a pointing device (e.g. mouse, pointer,\n * touch), turn off keyboard modality.\n * This accounts for situations where focus enters the page from the URL bar.\n * @param {Event} e\n */\n function onInitialPointerMove(e) {\n // Work around a Safari quirk that fires a mousemove on whenever the\n // window blurs, even if you're tabbing out of the page. \u00AF\\_(\u30C4)_/\u00AF\n if (e.target.nodeName && e.target.nodeName.toLowerCase() === 'html') {\n return;\n }\n\n hadKeyboardEvent = false;\n removeInitialPointerMoveListeners();\n }\n\n // For some kinds of state, we are interested in changes at the global scope\n // only. For example, global pointer input, global key presses and global\n // visibility change should affect the state at every scope:\n document.addEventListener('keydown', onKeyDown, true);\n document.addEventListener('mousedown', onPointerDown, true);\n document.addEventListener('pointerdown', onPointerDown, true);\n document.addEventListener('touchstart', onPointerDown, true);\n document.addEventListener('visibilitychange', onVisibilityChange, true);\n\n addInitialPointerMoveListeners();\n\n // For focus and blur, we specifically care about state changes in the local\n // scope. This is because focus / blur events that originate from within a\n // shadow root are not re-dispatched from the host element if it was already\n // the active element in its own scope:\n scope.addEventListener('focus', onFocus, true);\n scope.addEventListener('blur', onBlur, true);\n\n // We detect that a node is a ShadowRoot by ensuring that it is a\n // DocumentFragment and also has a host property. This check covers native\n // implementation and polyfill implementation transparently. If we only cared\n // about the native implementation, we could just check if the scope was\n // an instance of a ShadowRoot.\n if (scope.nodeType === Node.DOCUMENT_FRAGMENT_NODE && scope.host) {\n // Since a ShadowRoot is a special kind of DocumentFragment, it does not\n // have a root element to add a class to. So, we add this attribute to the\n // host element instead:\n scope.host.setAttribute('data-js-focus-visible', '');\n } else if (scope.nodeType === Node.DOCUMENT_NODE) {\n document.documentElement.classList.add('js-focus-visible');\n document.documentElement.setAttribute('data-js-focus-visible', '');\n }\n }\n\n // It is important to wrap all references to global window and document in\n // these checks to support server-side rendering use cases\n // @see https://github.com/WICG/focus-visible/issues/199\n if (typeof window !== 'undefined' && typeof document !== 'undefined') {\n // Make the polyfill helper globally available. This can be used as a signal\n // to interested libraries that wish to coordinate with the polyfill for e.g.,\n // applying the polyfill to a shadow root:\n window.applyFocusVisiblePolyfill = applyFocusVisiblePolyfill;\n\n // Notify interested libraries of the polyfill's presence, in case the\n // polyfill was loaded lazily:\n var event;\n\n try {\n event = new CustomEvent('focus-visible-polyfill-ready');\n } catch (error) {\n // IE11 does not support using CustomEvent as a constructor directly:\n event = document.createEvent('CustomEvent');\n event.initCustomEvent('focus-visible-polyfill-ready', false, false, {});\n }\n\n window.dispatchEvent(event);\n }\n\n if (typeof document !== 'undefined') {\n // Apply the polyfill to the global document, so that no JavaScript\n // coordination is required to use the polyfill in the top-level document:\n applyFocusVisiblePolyfill(document);\n }\n\n})));\n", "/*!\n * clipboard.js v2.0.11\n * https://clipboardjs.com/\n *\n * Licensed MIT \u00A9 Zeno Rocha\n */\n(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory();\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine([], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"ClipboardJS\"] = factory();\n\telse\n\t\troot[\"ClipboardJS\"] = factory();\n})(this, function() {\nreturn /******/ (function() { // webpackBootstrap\n/******/ \tvar __webpack_modules__ = ({\n\n/***/ 686:\n/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\n\n// EXPORTS\n__webpack_require__.d(__webpack_exports__, {\n \"default\": function() { return /* binding */ clipboard; }\n});\n\n// EXTERNAL MODULE: ./node_modules/tiny-emitter/index.js\nvar tiny_emitter = __webpack_require__(279);\nvar tiny_emitter_default = /*#__PURE__*/__webpack_require__.n(tiny_emitter);\n// EXTERNAL MODULE: ./node_modules/good-listener/src/listen.js\nvar listen = __webpack_require__(370);\nvar listen_default = /*#__PURE__*/__webpack_require__.n(listen);\n// EXTERNAL MODULE: ./node_modules/select/src/select.js\nvar src_select = __webpack_require__(817);\nvar select_default = /*#__PURE__*/__webpack_require__.n(src_select);\n;// CONCATENATED MODULE: ./src/common/command.js\n/**\n * Executes a given operation type.\n * @param {String} type\n * @return {Boolean}\n */\nfunction command(type) {\n try {\n return document.execCommand(type);\n } catch (err) {\n return false;\n }\n}\n;// CONCATENATED MODULE: ./src/actions/cut.js\n\n\n/**\n * Cut action wrapper.\n * @param {String|HTMLElement} target\n * @return {String}\n */\n\nvar ClipboardActionCut = function ClipboardActionCut(target) {\n var selectedText = select_default()(target);\n command('cut');\n return selectedText;\n};\n\n/* harmony default export */ var actions_cut = (ClipboardActionCut);\n;// CONCATENATED MODULE: ./src/common/create-fake-element.js\n/**\n * Creates a fake textarea element with a value.\n * @param {String} value\n * @return {HTMLElement}\n */\nfunction createFakeElement(value) {\n var isRTL = document.documentElement.getAttribute('dir') === 'rtl';\n var fakeElement = document.createElement('textarea'); // Prevent zooming on iOS\n\n fakeElement.style.fontSize = '12pt'; // Reset box model\n\n fakeElement.style.border = '0';\n fakeElement.style.padding = '0';\n fakeElement.style.margin = '0'; // Move element out of screen horizontally\n\n fakeElement.style.position = 'absolute';\n fakeElement.style[isRTL ? 'right' : 'left'] = '-9999px'; // Move element to the same position vertically\n\n var yPosition = window.pageYOffset || document.documentElement.scrollTop;\n fakeElement.style.top = \"\".concat(yPosition, \"px\");\n fakeElement.setAttribute('readonly', '');\n fakeElement.value = value;\n return fakeElement;\n}\n;// CONCATENATED MODULE: ./src/actions/copy.js\n\n\n\n/**\n * Create fake copy action wrapper using a fake element.\n * @param {String} target\n * @param {Object} options\n * @return {String}\n */\n\nvar fakeCopyAction = function fakeCopyAction(value, options) {\n var fakeElement = createFakeElement(value);\n options.container.appendChild(fakeElement);\n var selectedText = select_default()(fakeElement);\n command('copy');\n fakeElement.remove();\n return selectedText;\n};\n/**\n * Copy action wrapper.\n * @param {String|HTMLElement} target\n * @param {Object} options\n * @return {String}\n */\n\n\nvar ClipboardActionCopy = function ClipboardActionCopy(target) {\n var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {\n container: document.body\n };\n var selectedText = '';\n\n if (typeof target === 'string') {\n selectedText = fakeCopyAction(target, options);\n } else if (target instanceof HTMLInputElement && !['text', 'search', 'url', 'tel', 'password'].includes(target === null || target === void 0 ? void 0 : target.type)) {\n // If input type doesn't support `setSelectionRange`. Simulate it. https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/setSelectionRange\n selectedText = fakeCopyAction(target.value, options);\n } else {\n selectedText = select_default()(target);\n command('copy');\n }\n\n return selectedText;\n};\n\n/* harmony default export */ var actions_copy = (ClipboardActionCopy);\n;// CONCATENATED MODULE: ./src/actions/default.js\nfunction _typeof(obj) { \"@babel/helpers - typeof\"; if (typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; }; } return _typeof(obj); }\n\n\n\n/**\n * Inner function which performs selection from either `text` or `target`\n * properties and then executes copy or cut operations.\n * @param {Object} options\n */\n\nvar ClipboardActionDefault = function ClipboardActionDefault() {\n var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n // Defines base properties passed from constructor.\n var _options$action = options.action,\n action = _options$action === void 0 ? 'copy' : _options$action,\n container = options.container,\n target = options.target,\n text = options.text; // Sets the `action` to be performed which can be either 'copy' or 'cut'.\n\n if (action !== 'copy' && action !== 'cut') {\n throw new Error('Invalid \"action\" value, use either \"copy\" or \"cut\"');\n } // Sets the `target` property using an element that will be have its content copied.\n\n\n if (target !== undefined) {\n if (target && _typeof(target) === 'object' && target.nodeType === 1) {\n if (action === 'copy' && target.hasAttribute('disabled')) {\n throw new Error('Invalid \"target\" attribute. Please use \"readonly\" instead of \"disabled\" attribute');\n }\n\n if (action === 'cut' && (target.hasAttribute('readonly') || target.hasAttribute('disabled'))) {\n throw new Error('Invalid \"target\" attribute. You can\\'t cut text from elements with \"readonly\" or \"disabled\" attributes');\n }\n } else {\n throw new Error('Invalid \"target\" value, use a valid Element');\n }\n } // Define selection strategy based on `text` property.\n\n\n if (text) {\n return actions_copy(text, {\n container: container\n });\n } // Defines which selection strategy based on `target` property.\n\n\n if (target) {\n return action === 'cut' ? actions_cut(target) : actions_copy(target, {\n container: container\n });\n }\n};\n\n/* harmony default export */ var actions_default = (ClipboardActionDefault);\n;// CONCATENATED MODULE: ./src/clipboard.js\nfunction clipboard_typeof(obj) { \"@babel/helpers - typeof\"; if (typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\") { clipboard_typeof = function _typeof(obj) { return typeof obj; }; } else { clipboard_typeof = function _typeof(obj) { return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; }; } return clipboard_typeof(obj); }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }\n\nfunction _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function\"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }\n\nfunction _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }\n\nfunction _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }\n\nfunction _possibleConstructorReturn(self, call) { if (call && (clipboard_typeof(call) === \"object\" || typeof call === \"function\")) { return call; } return _assertThisInitialized(self); }\n\nfunction _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return self; }\n\nfunction _isNativeReflectConstruct() { if (typeof Reflect === \"undefined\" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === \"function\") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } }\n\nfunction _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }\n\n\n\n\n\n\n/**\n * Helper function to retrieve attribute value.\n * @param {String} suffix\n * @param {Element} element\n */\n\nfunction getAttributeValue(suffix, element) {\n var attribute = \"data-clipboard-\".concat(suffix);\n\n if (!element.hasAttribute(attribute)) {\n return;\n }\n\n return element.getAttribute(attribute);\n}\n/**\n * Base class which takes one or more elements, adds event listeners to them,\n * and instantiates a new `ClipboardAction` on each click.\n */\n\n\nvar Clipboard = /*#__PURE__*/function (_Emitter) {\n _inherits(Clipboard, _Emitter);\n\n var _super = _createSuper(Clipboard);\n\n /**\n * @param {String|HTMLElement|HTMLCollection|NodeList} trigger\n * @param {Object} options\n */\n function Clipboard(trigger, options) {\n var _this;\n\n _classCallCheck(this, Clipboard);\n\n _this = _super.call(this);\n\n _this.resolveOptions(options);\n\n _this.listenClick(trigger);\n\n return _this;\n }\n /**\n * Defines if attributes would be resolved using internal setter functions\n * or custom functions that were passed in the constructor.\n * @param {Object} options\n */\n\n\n _createClass(Clipboard, [{\n key: \"resolveOptions\",\n value: function resolveOptions() {\n var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n this.action = typeof options.action === 'function' ? options.action : this.defaultAction;\n this.target = typeof options.target === 'function' ? options.target : this.defaultTarget;\n this.text = typeof options.text === 'function' ? options.text : this.defaultText;\n this.container = clipboard_typeof(options.container) === 'object' ? options.container : document.body;\n }\n /**\n * Adds a click event listener to the passed trigger.\n * @param {String|HTMLElement|HTMLCollection|NodeList} trigger\n */\n\n }, {\n key: \"listenClick\",\n value: function listenClick(trigger) {\n var _this2 = this;\n\n this.listener = listen_default()(trigger, 'click', function (e) {\n return _this2.onClick(e);\n });\n }\n /**\n * Defines a new `ClipboardAction` on each click event.\n * @param {Event} e\n */\n\n }, {\n key: \"onClick\",\n value: function onClick(e) {\n var trigger = e.delegateTarget || e.currentTarget;\n var action = this.action(trigger) || 'copy';\n var text = actions_default({\n action: action,\n container: this.container,\n target: this.target(trigger),\n text: this.text(trigger)\n }); // Fires an event based on the copy operation result.\n\n this.emit(text ? 'success' : 'error', {\n action: action,\n text: text,\n trigger: trigger,\n clearSelection: function clearSelection() {\n if (trigger) {\n trigger.focus();\n }\n\n window.getSelection().removeAllRanges();\n }\n });\n }\n /**\n * Default `action` lookup function.\n * @param {Element} trigger\n */\n\n }, {\n key: \"defaultAction\",\n value: function defaultAction(trigger) {\n return getAttributeValue('action', trigger);\n }\n /**\n * Default `target` lookup function.\n * @param {Element} trigger\n */\n\n }, {\n key: \"defaultTarget\",\n value: function defaultTarget(trigger) {\n var selector = getAttributeValue('target', trigger);\n\n if (selector) {\n return document.querySelector(selector);\n }\n }\n /**\n * Allow fire programmatically a copy action\n * @param {String|HTMLElement} target\n * @param {Object} options\n * @returns Text copied.\n */\n\n }, {\n key: \"defaultText\",\n\n /**\n * Default `text` lookup function.\n * @param {Element} trigger\n */\n value: function defaultText(trigger) {\n return getAttributeValue('text', trigger);\n }\n /**\n * Destroy lifecycle.\n */\n\n }, {\n key: \"destroy\",\n value: function destroy() {\n this.listener.destroy();\n }\n }], [{\n key: \"copy\",\n value: function copy(target) {\n var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {\n container: document.body\n };\n return actions_copy(target, options);\n }\n /**\n * Allow fire programmatically a cut action\n * @param {String|HTMLElement} target\n * @returns Text cutted.\n */\n\n }, {\n key: \"cut\",\n value: function cut(target) {\n return actions_cut(target);\n }\n /**\n * Returns the support of the given action, or all actions if no action is\n * given.\n * @param {String} [action]\n */\n\n }, {\n key: \"isSupported\",\n value: function isSupported() {\n var action = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ['copy', 'cut'];\n var actions = typeof action === 'string' ? [action] : action;\n var support = !!document.queryCommandSupported;\n actions.forEach(function (action) {\n support = support && !!document.queryCommandSupported(action);\n });\n return support;\n }\n }]);\n\n return Clipboard;\n}((tiny_emitter_default()));\n\n/* harmony default export */ var clipboard = (Clipboard);\n\n/***/ }),\n\n/***/ 828:\n/***/ (function(module) {\n\nvar DOCUMENT_NODE_TYPE = 9;\n\n/**\n * A polyfill for Element.matches()\n */\nif (typeof Element !== 'undefined' && !Element.prototype.matches) {\n var proto = Element.prototype;\n\n proto.matches = proto.matchesSelector ||\n proto.mozMatchesSelector ||\n proto.msMatchesSelector ||\n proto.oMatchesSelector ||\n proto.webkitMatchesSelector;\n}\n\n/**\n * Finds the closest parent that matches a selector.\n *\n * @param {Element} element\n * @param {String} selector\n * @return {Function}\n */\nfunction closest (element, selector) {\n while (element && element.nodeType !== DOCUMENT_NODE_TYPE) {\n if (typeof element.matches === 'function' &&\n element.matches(selector)) {\n return element;\n }\n element = element.parentNode;\n }\n}\n\nmodule.exports = closest;\n\n\n/***/ }),\n\n/***/ 438:\n/***/ (function(module, __unused_webpack_exports, __webpack_require__) {\n\nvar closest = __webpack_require__(828);\n\n/**\n * Delegates event to a selector.\n *\n * @param {Element} element\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @param {Boolean} useCapture\n * @return {Object}\n */\nfunction _delegate(element, selector, type, callback, useCapture) {\n var listenerFn = listener.apply(this, arguments);\n\n element.addEventListener(type, listenerFn, useCapture);\n\n return {\n destroy: function() {\n element.removeEventListener(type, listenerFn, useCapture);\n }\n }\n}\n\n/**\n * Delegates event to a selector.\n *\n * @param {Element|String|Array} [elements]\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @param {Boolean} useCapture\n * @return {Object}\n */\nfunction delegate(elements, selector, type, callback, useCapture) {\n // Handle the regular Element usage\n if (typeof elements.addEventListener === 'function') {\n return _delegate.apply(null, arguments);\n }\n\n // Handle Element-less usage, it defaults to global delegation\n if (typeof type === 'function') {\n // Use `document` as the first parameter, then apply arguments\n // This is a short way to .unshift `arguments` without running into deoptimizations\n return _delegate.bind(null, document).apply(null, arguments);\n }\n\n // Handle Selector-based usage\n if (typeof elements === 'string') {\n elements = document.querySelectorAll(elements);\n }\n\n // Handle Array-like based usage\n return Array.prototype.map.call(elements, function (element) {\n return _delegate(element, selector, type, callback, useCapture);\n });\n}\n\n/**\n * Finds closest match and invokes callback.\n *\n * @param {Element} element\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @return {Function}\n */\nfunction listener(element, selector, type, callback) {\n return function(e) {\n e.delegateTarget = closest(e.target, selector);\n\n if (e.delegateTarget) {\n callback.call(element, e);\n }\n }\n}\n\nmodule.exports = delegate;\n\n\n/***/ }),\n\n/***/ 879:\n/***/ (function(__unused_webpack_module, exports) {\n\n/**\n * Check if argument is a HTML element.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.node = function(value) {\n return value !== undefined\n && value instanceof HTMLElement\n && value.nodeType === 1;\n};\n\n/**\n * Check if argument is a list of HTML elements.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.nodeList = function(value) {\n var type = Object.prototype.toString.call(value);\n\n return value !== undefined\n && (type === '[object NodeList]' || type === '[object HTMLCollection]')\n && ('length' in value)\n && (value.length === 0 || exports.node(value[0]));\n};\n\n/**\n * Check if argument is a string.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.string = function(value) {\n return typeof value === 'string'\n || value instanceof String;\n};\n\n/**\n * Check if argument is a function.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.fn = function(value) {\n var type = Object.prototype.toString.call(value);\n\n return type === '[object Function]';\n};\n\n\n/***/ }),\n\n/***/ 370:\n/***/ (function(module, __unused_webpack_exports, __webpack_require__) {\n\nvar is = __webpack_require__(879);\nvar delegate = __webpack_require__(438);\n\n/**\n * Validates all params and calls the right\n * listener function based on its target type.\n *\n * @param {String|HTMLElement|HTMLCollection|NodeList} target\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listen(target, type, callback) {\n if (!target && !type && !callback) {\n throw new Error('Missing required arguments');\n }\n\n if (!is.string(type)) {\n throw new TypeError('Second argument must be a String');\n }\n\n if (!is.fn(callback)) {\n throw new TypeError('Third argument must be a Function');\n }\n\n if (is.node(target)) {\n return listenNode(target, type, callback);\n }\n else if (is.nodeList(target)) {\n return listenNodeList(target, type, callback);\n }\n else if (is.string(target)) {\n return listenSelector(target, type, callback);\n }\n else {\n throw new TypeError('First argument must be a String, HTMLElement, HTMLCollection, or NodeList');\n }\n}\n\n/**\n * Adds an event listener to a HTML element\n * and returns a remove listener function.\n *\n * @param {HTMLElement} node\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listenNode(node, type, callback) {\n node.addEventListener(type, callback);\n\n return {\n destroy: function() {\n node.removeEventListener(type, callback);\n }\n }\n}\n\n/**\n * Add an event listener to a list of HTML elements\n * and returns a remove listener function.\n *\n * @param {NodeList|HTMLCollection} nodeList\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listenNodeList(nodeList, type, callback) {\n Array.prototype.forEach.call(nodeList, function(node) {\n node.addEventListener(type, callback);\n });\n\n return {\n destroy: function() {\n Array.prototype.forEach.call(nodeList, function(node) {\n node.removeEventListener(type, callback);\n });\n }\n }\n}\n\n/**\n * Add an event listener to a selector\n * and returns a remove listener function.\n *\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listenSelector(selector, type, callback) {\n return delegate(document.body, selector, type, callback);\n}\n\nmodule.exports = listen;\n\n\n/***/ }),\n\n/***/ 817:\n/***/ (function(module) {\n\nfunction select(element) {\n var selectedText;\n\n if (element.nodeName === 'SELECT') {\n element.focus();\n\n selectedText = element.value;\n }\n else if (element.nodeName === 'INPUT' || element.nodeName === 'TEXTAREA') {\n var isReadOnly = element.hasAttribute('readonly');\n\n if (!isReadOnly) {\n element.setAttribute('readonly', '');\n }\n\n element.select();\n element.setSelectionRange(0, element.value.length);\n\n if (!isReadOnly) {\n element.removeAttribute('readonly');\n }\n\n selectedText = element.value;\n }\n else {\n if (element.hasAttribute('contenteditable')) {\n element.focus();\n }\n\n var selection = window.getSelection();\n var range = document.createRange();\n\n range.selectNodeContents(element);\n selection.removeAllRanges();\n selection.addRange(range);\n\n selectedText = selection.toString();\n }\n\n return selectedText;\n}\n\nmodule.exports = select;\n\n\n/***/ }),\n\n/***/ 279:\n/***/ (function(module) {\n\nfunction E () {\n // Keep this empty so it's easier to inherit from\n // (via https://github.com/lipsmack from https://github.com/scottcorgan/tiny-emitter/issues/3)\n}\n\nE.prototype = {\n on: function (name, callback, ctx) {\n var e = this.e || (this.e = {});\n\n (e[name] || (e[name] = [])).push({\n fn: callback,\n ctx: ctx\n });\n\n return this;\n },\n\n once: function (name, callback, ctx) {\n var self = this;\n function listener () {\n self.off(name, listener);\n callback.apply(ctx, arguments);\n };\n\n listener._ = callback\n return this.on(name, listener, ctx);\n },\n\n emit: function (name) {\n var data = [].slice.call(arguments, 1);\n var evtArr = ((this.e || (this.e = {}))[name] || []).slice();\n var i = 0;\n var len = evtArr.length;\n\n for (i; i < len; i++) {\n evtArr[i].fn.apply(evtArr[i].ctx, data);\n }\n\n return this;\n },\n\n off: function (name, callback) {\n var e = this.e || (this.e = {});\n var evts = e[name];\n var liveEvents = [];\n\n if (evts && callback) {\n for (var i = 0, len = evts.length; i < len; i++) {\n if (evts[i].fn !== callback && evts[i].fn._ !== callback)\n liveEvents.push(evts[i]);\n }\n }\n\n // Remove event from queue to prevent memory leak\n // Suggested by https://github.com/lazd\n // Ref: https://github.com/scottcorgan/tiny-emitter/commit/c6ebfaa9bc973b33d110a84a307742b7cf94c953#commitcomment-5024910\n\n (liveEvents.length)\n ? e[name] = liveEvents\n : delete e[name];\n\n return this;\n }\n};\n\nmodule.exports = E;\nmodule.exports.TinyEmitter = E;\n\n\n/***/ })\n\n/******/ \t});\n/************************************************************************/\n/******/ \t// The module cache\n/******/ \tvar __webpack_module_cache__ = {};\n/******/ \t\n/******/ \t// The require function\n/******/ \tfunction __webpack_require__(moduleId) {\n/******/ \t\t// Check if module is in cache\n/******/ \t\tif(__webpack_module_cache__[moduleId]) {\n/******/ \t\t\treturn __webpack_module_cache__[moduleId].exports;\n/******/ \t\t}\n/******/ \t\t// Create a new module (and put it into the cache)\n/******/ \t\tvar module = __webpack_module_cache__[moduleId] = {\n/******/ \t\t\t// no module.id needed\n/******/ \t\t\t// no module.loaded needed\n/******/ \t\t\texports: {}\n/******/ \t\t};\n/******/ \t\n/******/ \t\t// Execute the module function\n/******/ \t\t__webpack_modules__[moduleId](module, module.exports, __webpack_require__);\n/******/ \t\n/******/ \t\t// Return the exports of the module\n/******/ \t\treturn module.exports;\n/******/ \t}\n/******/ \t\n/************************************************************************/\n/******/ \t/* webpack/runtime/compat get default export */\n/******/ \t!function() {\n/******/ \t\t// getDefaultExport function for compatibility with non-harmony modules\n/******/ \t\t__webpack_require__.n = function(module) {\n/******/ \t\t\tvar getter = module && module.__esModule ?\n/******/ \t\t\t\tfunction() { return module['default']; } :\n/******/ \t\t\t\tfunction() { return module; };\n/******/ \t\t\t__webpack_require__.d(getter, { a: getter });\n/******/ \t\t\treturn getter;\n/******/ \t\t};\n/******/ \t}();\n/******/ \t\n/******/ \t/* webpack/runtime/define property getters */\n/******/ \t!function() {\n/******/ \t\t// define getter functions for harmony exports\n/******/ \t\t__webpack_require__.d = function(exports, definition) {\n/******/ \t\t\tfor(var key in definition) {\n/******/ \t\t\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n/******/ \t\t\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n/******/ \t\t\t\t}\n/******/ \t\t\t}\n/******/ \t\t};\n/******/ \t}();\n/******/ \t\n/******/ \t/* webpack/runtime/hasOwnProperty shorthand */\n/******/ \t!function() {\n/******/ \t\t__webpack_require__.o = function(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); }\n/******/ \t}();\n/******/ \t\n/************************************************************************/\n/******/ \t// module exports must be returned from runtime so entry inlining is disabled\n/******/ \t// startup\n/******/ \t// Load entry module and return exports\n/******/ \treturn __webpack_require__(686);\n/******/ })()\n.default;\n});", "/*!\n * escape-html\n * Copyright(c) 2012-2013 TJ Holowaychuk\n * Copyright(c) 2015 Andreas Lubbe\n * Copyright(c) 2015 Tiancheng \"Timothy\" Gu\n * MIT Licensed\n */\n\n'use strict';\n\n/**\n * Module variables.\n * @private\n */\n\nvar matchHtmlRegExp = /[\"'&<>]/;\n\n/**\n * Module exports.\n * @public\n */\n\nmodule.exports = escapeHtml;\n\n/**\n * Escape special characters in the given string of html.\n *\n * @param {string} string The string to escape for inserting into HTML\n * @return {string}\n * @public\n */\n\nfunction escapeHtml(string) {\n var str = '' + string;\n var match = matchHtmlRegExp.exec(str);\n\n if (!match) {\n return str;\n }\n\n var escape;\n var html = '';\n var index = 0;\n var lastIndex = 0;\n\n for (index = match.index; index < str.length; index++) {\n switch (str.charCodeAt(index)) {\n case 34: // \"\n escape = '"';\n break;\n case 38: // &\n escape = '&';\n break;\n case 39: // '\n escape = ''';\n break;\n case 60: // <\n escape = '<';\n break;\n case 62: // >\n escape = '>';\n break;\n default:\n continue;\n }\n\n if (lastIndex !== index) {\n html += str.substring(lastIndex, index);\n }\n\n lastIndex = index + 1;\n html += escape;\n }\n\n return lastIndex !== index\n ? html + str.substring(lastIndex, index)\n : html;\n}\n", "/*\n * Copyright (c) 2016-2024 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport \"focus-visible\"\n\nimport {\n EMPTY,\n NEVER,\n Observable,\n Subject,\n defer,\n delay,\n filter,\n map,\n merge,\n mergeWith,\n shareReplay,\n switchMap\n} from \"rxjs\"\n\nimport { configuration, feature } from \"./_\"\nimport {\n at,\n getActiveElement,\n getOptionalElement,\n requestJSON,\n setLocation,\n setToggle,\n watchDocument,\n watchKeyboard,\n watchLocation,\n watchLocationTarget,\n watchMedia,\n watchPrint,\n watchScript,\n watchViewport\n} from \"./browser\"\nimport {\n getComponentElement,\n getComponentElements,\n mountAnnounce,\n mountBackToTop,\n mountConsent,\n mountContent,\n mountDialog,\n mountHeader,\n mountHeaderTitle,\n mountPalette,\n mountProgress,\n mountSearch,\n mountSearchHiglight,\n mountSidebar,\n mountSource,\n mountTableOfContents,\n mountTabs,\n watchHeader,\n watchMain\n} from \"./components\"\nimport {\n SearchIndex,\n setupClipboardJS,\n setupInstantNavigation,\n setupVersionSelector\n} from \"./integrations\"\nimport {\n patchEllipsis,\n patchIndeterminate,\n patchScrollfix,\n patchScrolllock\n} from \"./patches\"\nimport \"./polyfills\"\n\n/* ----------------------------------------------------------------------------\n * Functions - @todo refactor\n * ------------------------------------------------------------------------- */\n\n/**\n * Fetch search index\n *\n * @returns Search index observable\n */\nfunction fetchSearchIndex(): Observable {\n if (location.protocol === \"file:\") {\n return watchScript(\n `${new URL(\"search/search_index.js\", config.base)}`\n )\n .pipe(\n // @ts-ignore - @todo fix typings\n map(() => __index),\n shareReplay(1)\n )\n } else {\n return requestJSON(\n new URL(\"search/search_index.json\", config.base)\n )\n }\n}\n\n/* ----------------------------------------------------------------------------\n * Application\n * ------------------------------------------------------------------------- */\n\n/* Yay, JavaScript is available */\ndocument.documentElement.classList.remove(\"no-js\")\ndocument.documentElement.classList.add(\"js\")\n\n/* Set up navigation observables and subjects */\nconst document$ = watchDocument()\nconst location$ = watchLocation()\nconst target$ = watchLocationTarget(location$)\nconst keyboard$ = watchKeyboard()\n\n/* Set up media observables */\nconst viewport$ = watchViewport()\nconst tablet$ = watchMedia(\"(min-width: 960px)\")\nconst screen$ = watchMedia(\"(min-width: 1220px)\")\nconst print$ = watchPrint()\n\n/* Retrieve search index, if search is enabled */\nconst config = configuration()\nconst index$ = document.forms.namedItem(\"search\")\n ? fetchSearchIndex()\n : NEVER\n\n/* Set up Clipboard.js integration */\nconst alert$ = new Subject()\nsetupClipboardJS({ alert$ })\n\n/* Set up progress indicator */\nconst progress$ = new Subject()\n\n/* Set up instant navigation, if enabled */\nif (feature(\"navigation.instant\"))\n setupInstantNavigation({ location$, viewport$, progress$ })\n .subscribe(document$)\n\n/* Set up version selector */\nif (config.version?.provider === \"mike\")\n setupVersionSelector({ document$ })\n\n/* Always close drawer and search on navigation */\nmerge(location$, target$)\n .pipe(\n delay(125)\n )\n .subscribe(() => {\n setToggle(\"drawer\", false)\n setToggle(\"search\", false)\n })\n\n/* Set up global keyboard handlers */\nkeyboard$\n .pipe(\n filter(({ mode }) => mode === \"global\")\n )\n .subscribe(key => {\n switch (key.type) {\n\n /* Go to previous page */\n case \"p\":\n case \",\":\n const prev = getOptionalElement(\"link[rel=prev]\")\n if (typeof prev !== \"undefined\")\n setLocation(prev)\n break\n\n /* Go to next page */\n case \"n\":\n case \".\":\n const next = getOptionalElement(\"link[rel=next]\")\n if (typeof next !== \"undefined\")\n setLocation(next)\n break\n\n /* Expand navigation, see https://bit.ly/3ZjG5io */\n case \"Enter\":\n const active = getActiveElement()\n if (active instanceof HTMLLabelElement)\n active.click()\n }\n })\n\n/* Set up patches */\npatchEllipsis({ document$ })\npatchIndeterminate({ document$, tablet$ })\npatchScrollfix({ document$ })\npatchScrolllock({ viewport$, tablet$ })\n\n/* Set up header and main area observable */\nconst header$ = watchHeader(getComponentElement(\"header\"), { viewport$ })\nconst main$ = document$\n .pipe(\n map(() => getComponentElement(\"main\")),\n switchMap(el => watchMain(el, { viewport$, header$ })),\n shareReplay(1)\n )\n\n/* Set up control component observables */\nconst control$ = merge(\n\n /* Consent */\n ...getComponentElements(\"consent\")\n .map(el => mountConsent(el, { target$ })),\n\n /* Dialog */\n ...getComponentElements(\"dialog\")\n .map(el => mountDialog(el, { alert$ })),\n\n /* Header */\n ...getComponentElements(\"header\")\n .map(el => mountHeader(el, { viewport$, header$, main$ })),\n\n /* Color palette */\n ...getComponentElements(\"palette\")\n .map(el => mountPalette(el)),\n\n /* Progress bar */\n ...getComponentElements(\"progress\")\n .map(el => mountProgress(el, { progress$ })),\n\n /* Search */\n ...getComponentElements(\"search\")\n .map(el => mountSearch(el, { index$, keyboard$ })),\n\n /* Repository information */\n ...getComponentElements(\"source\")\n .map(el => mountSource(el))\n)\n\n/* Set up content component observables */\nconst content$ = defer(() => merge(\n\n /* Announcement bar */\n ...getComponentElements(\"announce\")\n .map(el => mountAnnounce(el)),\n\n /* Content */\n ...getComponentElements(\"content\")\n .map(el => mountContent(el, { viewport$, target$, print$ })),\n\n /* Search highlighting */\n ...getComponentElements(\"content\")\n .map(el => feature(\"search.highlight\")\n ? mountSearchHiglight(el, { index$, location$ })\n : EMPTY\n ),\n\n /* Header title */\n ...getComponentElements(\"header-title\")\n .map(el => mountHeaderTitle(el, { viewport$, header$ })),\n\n /* Sidebar */\n ...getComponentElements(\"sidebar\")\n .map(el => el.getAttribute(\"data-md-type\") === \"navigation\"\n ? at(screen$, () => mountSidebar(el, { viewport$, header$, main$ }))\n : at(tablet$, () => mountSidebar(el, { viewport$, header$, main$ }))\n ),\n\n /* Navigation tabs */\n ...getComponentElements(\"tabs\")\n .map(el => mountTabs(el, { viewport$, header$ })),\n\n /* Table of contents */\n ...getComponentElements(\"toc\")\n .map(el => mountTableOfContents(el, {\n viewport$, header$, main$, target$\n })),\n\n /* Back-to-top button */\n ...getComponentElements(\"top\")\n .map(el => mountBackToTop(el, { viewport$, header$, main$, target$ }))\n))\n\n/* Set up component observables */\nconst component$ = document$\n .pipe(\n switchMap(() => content$),\n mergeWith(control$),\n shareReplay(1)\n )\n\n/* Subscribe to all components */\ncomponent$.subscribe()\n\n/* ----------------------------------------------------------------------------\n * Exports\n * ------------------------------------------------------------------------- */\n\nwindow.document$ = document$ /* Document observable */\nwindow.location$ = location$ /* Location subject */\nwindow.target$ = target$ /* Location target observable */\nwindow.keyboard$ = keyboard$ /* Keyboard observable */\nwindow.viewport$ = viewport$ /* Viewport observable */\nwindow.tablet$ = tablet$ /* Media tablet observable */\nwindow.screen$ = screen$ /* Media screen observable */\nwindow.print$ = print$ /* Media print observable */\nwindow.alert$ = alert$ /* Alert subject */\nwindow.progress$ = progress$ /* Progress indicator subject */\nwindow.component$ = component$ /* Component observable */\n", "/*! *****************************************************************************\r\nCopyright (c) Microsoft Corporation.\r\n\r\nPermission to use, copy, modify, and/or distribute this software for any\r\npurpose with or without fee is hereby granted.\r\n\r\nTHE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH\r\nREGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY\r\nAND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,\r\nINDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM\r\nLOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR\r\nOTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR\r\nPERFORMANCE OF THIS SOFTWARE.\r\n***************************************************************************** */\r\n/* global Reflect, Promise */\r\n\r\nvar extendStatics = function(d, b) {\r\n extendStatics = Object.setPrototypeOf ||\r\n ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\r\n function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };\r\n return extendStatics(d, b);\r\n};\r\n\r\nexport function __extends(d, b) {\r\n if (typeof b !== \"function\" && b !== null)\r\n throw new TypeError(\"Class extends value \" + String(b) + \" is not a constructor or null\");\r\n extendStatics(d, b);\r\n function __() { this.constructor = d; }\r\n d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\r\n}\r\n\r\nexport var __assign = function() {\r\n __assign = Object.assign || function __assign(t) {\r\n for (var s, i = 1, n = arguments.length; i < n; i++) {\r\n s = arguments[i];\r\n for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];\r\n }\r\n return t;\r\n }\r\n return __assign.apply(this, arguments);\r\n}\r\n\r\nexport function __rest(s, e) {\r\n var t = {};\r\n for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)\r\n t[p] = s[p];\r\n if (s != null && typeof Object.getOwnPropertySymbols === \"function\")\r\n for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {\r\n if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))\r\n t[p[i]] = s[p[i]];\r\n }\r\n return t;\r\n}\r\n\r\nexport function __decorate(decorators, target, key, desc) {\r\n var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;\r\n if (typeof Reflect === \"object\" && typeof Reflect.decorate === \"function\") r = Reflect.decorate(decorators, target, key, desc);\r\n else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;\r\n return c > 3 && r && Object.defineProperty(target, key, r), r;\r\n}\r\n\r\nexport function __param(paramIndex, decorator) {\r\n return function (target, key) { decorator(target, key, paramIndex); }\r\n}\r\n\r\nexport function __metadata(metadataKey, metadataValue) {\r\n if (typeof Reflect === \"object\" && typeof Reflect.metadata === \"function\") return Reflect.metadata(metadataKey, metadataValue);\r\n}\r\n\r\nexport function __awaiter(thisArg, _arguments, P, generator) {\r\n function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\r\n return new (P || (P = Promise))(function (resolve, reject) {\r\n function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\r\n function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\r\n function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\r\n step((generator = generator.apply(thisArg, _arguments || [])).next());\r\n });\r\n}\r\n\r\nexport function __generator(thisArg, body) {\r\n var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;\r\n return g = { next: verb(0), \"throw\": verb(1), \"return\": verb(2) }, typeof Symbol === \"function\" && (g[Symbol.iterator] = function() { return this; }), g;\r\n function verb(n) { return function (v) { return step([n, v]); }; }\r\n function step(op) {\r\n if (f) throw new TypeError(\"Generator is already executing.\");\r\n while (_) try {\r\n if (f = 1, y && (t = op[0] & 2 ? y[\"return\"] : op[0] ? y[\"throw\"] || ((t = y[\"return\"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;\r\n if (y = 0, t) op = [op[0] & 2, t.value];\r\n switch (op[0]) {\r\n case 0: case 1: t = op; break;\r\n case 4: _.label++; return { value: op[1], done: false };\r\n case 5: _.label++; y = op[1]; op = [0]; continue;\r\n case 7: op = _.ops.pop(); _.trys.pop(); continue;\r\n default:\r\n if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }\r\n if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }\r\n if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }\r\n if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }\r\n if (t[2]) _.ops.pop();\r\n _.trys.pop(); continue;\r\n }\r\n op = body.call(thisArg, _);\r\n } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }\r\n if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };\r\n }\r\n}\r\n\r\nexport var __createBinding = Object.create ? (function(o, m, k, k2) {\r\n if (k2 === undefined) k2 = k;\r\n Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });\r\n}) : (function(o, m, k, k2) {\r\n if (k2 === undefined) k2 = k;\r\n o[k2] = m[k];\r\n});\r\n\r\nexport function __exportStar(m, o) {\r\n for (var p in m) if (p !== \"default\" && !Object.prototype.hasOwnProperty.call(o, p)) __createBinding(o, m, p);\r\n}\r\n\r\nexport function __values(o) {\r\n var s = typeof Symbol === \"function\" && Symbol.iterator, m = s && o[s], i = 0;\r\n if (m) return m.call(o);\r\n if (o && typeof o.length === \"number\") return {\r\n next: function () {\r\n if (o && i >= o.length) o = void 0;\r\n return { value: o && o[i++], done: !o };\r\n }\r\n };\r\n throw new TypeError(s ? \"Object is not iterable.\" : \"Symbol.iterator is not defined.\");\r\n}\r\n\r\nexport function __read(o, n) {\r\n var m = typeof Symbol === \"function\" && o[Symbol.iterator];\r\n if (!m) return o;\r\n var i = m.call(o), r, ar = [], e;\r\n try {\r\n while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);\r\n }\r\n catch (error) { e = { error: error }; }\r\n finally {\r\n try {\r\n if (r && !r.done && (m = i[\"return\"])) m.call(i);\r\n }\r\n finally { if (e) throw e.error; }\r\n }\r\n return ar;\r\n}\r\n\r\n/** @deprecated */\r\nexport function __spread() {\r\n for (var ar = [], i = 0; i < arguments.length; i++)\r\n ar = ar.concat(__read(arguments[i]));\r\n return ar;\r\n}\r\n\r\n/** @deprecated */\r\nexport function __spreadArrays() {\r\n for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;\r\n for (var r = Array(s), k = 0, i = 0; i < il; i++)\r\n for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)\r\n r[k] = a[j];\r\n return r;\r\n}\r\n\r\nexport function __spreadArray(to, from, pack) {\r\n if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {\r\n if (ar || !(i in from)) {\r\n if (!ar) ar = Array.prototype.slice.call(from, 0, i);\r\n ar[i] = from[i];\r\n }\r\n }\r\n return to.concat(ar || Array.prototype.slice.call(from));\r\n}\r\n\r\nexport function __await(v) {\r\n return this instanceof __await ? (this.v = v, this) : new __await(v);\r\n}\r\n\r\nexport function __asyncGenerator(thisArg, _arguments, generator) {\r\n if (!Symbol.asyncIterator) throw new TypeError(\"Symbol.asyncIterator is not defined.\");\r\n var g = generator.apply(thisArg, _arguments || []), i, q = [];\r\n return i = {}, verb(\"next\"), verb(\"throw\"), verb(\"return\"), i[Symbol.asyncIterator] = function () { return this; }, i;\r\n function verb(n) { if (g[n]) i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; }\r\n function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } }\r\n function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); }\r\n function fulfill(value) { resume(\"next\", value); }\r\n function reject(value) { resume(\"throw\", value); }\r\n function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); }\r\n}\r\n\r\nexport function __asyncDelegator(o) {\r\n var i, p;\r\n return i = {}, verb(\"next\"), verb(\"throw\", function (e) { throw e; }), verb(\"return\"), i[Symbol.iterator] = function () { return this; }, i;\r\n function verb(n, f) { i[n] = o[n] ? function (v) { return (p = !p) ? { value: __await(o[n](v)), done: n === \"return\" } : f ? f(v) : v; } : f; }\r\n}\r\n\r\nexport function __asyncValues(o) {\r\n if (!Symbol.asyncIterator) throw new TypeError(\"Symbol.asyncIterator is not defined.\");\r\n var m = o[Symbol.asyncIterator], i;\r\n return m ? m.call(o) : (o = typeof __values === \"function\" ? __values(o) : o[Symbol.iterator](), i = {}, verb(\"next\"), verb(\"throw\"), verb(\"return\"), i[Symbol.asyncIterator] = function () { return this; }, i);\r\n function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }\r\n function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }\r\n}\r\n\r\nexport function __makeTemplateObject(cooked, raw) {\r\n if (Object.defineProperty) { Object.defineProperty(cooked, \"raw\", { value: raw }); } else { cooked.raw = raw; }\r\n return cooked;\r\n};\r\n\r\nvar __setModuleDefault = Object.create ? (function(o, v) {\r\n Object.defineProperty(o, \"default\", { enumerable: true, value: v });\r\n}) : function(o, v) {\r\n o[\"default\"] = v;\r\n};\r\n\r\nexport function __importStar(mod) {\r\n if (mod && mod.__esModule) return mod;\r\n var result = {};\r\n if (mod != null) for (var k in mod) if (k !== \"default\" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);\r\n __setModuleDefault(result, mod);\r\n return result;\r\n}\r\n\r\nexport function __importDefault(mod) {\r\n return (mod && mod.__esModule) ? mod : { default: mod };\r\n}\r\n\r\nexport function __classPrivateFieldGet(receiver, state, kind, f) {\r\n if (kind === \"a\" && !f) throw new TypeError(\"Private accessor was defined without a getter\");\r\n if (typeof state === \"function\" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError(\"Cannot read private member from an object whose class did not declare it\");\r\n return kind === \"m\" ? f : kind === \"a\" ? f.call(receiver) : f ? f.value : state.get(receiver);\r\n}\r\n\r\nexport function __classPrivateFieldSet(receiver, state, value, kind, f) {\r\n if (kind === \"m\") throw new TypeError(\"Private method is not writable\");\r\n if (kind === \"a\" && !f) throw new TypeError(\"Private accessor was defined without a setter\");\r\n if (typeof state === \"function\" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError(\"Cannot write private member to an object whose class did not declare it\");\r\n return (kind === \"a\" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;\r\n}\r\n", "/**\n * Returns true if the object is a function.\n * @param value The value to check\n */\nexport function isFunction(value: any): value is (...args: any[]) => any {\n return typeof value === 'function';\n}\n", "/**\n * Used to create Error subclasses until the community moves away from ES5.\n *\n * This is because compiling from TypeScript down to ES5 has issues with subclassing Errors\n * as well as other built-in types: https://github.com/Microsoft/TypeScript/issues/12123\n *\n * @param createImpl A factory function to create the actual constructor implementation. The returned\n * function should be a named function that calls `_super` internally.\n */\nexport function createErrorClass(createImpl: (_super: any) => any): T {\n const _super = (instance: any) => {\n Error.call(instance);\n instance.stack = new Error().stack;\n };\n\n const ctorFunc = createImpl(_super);\n ctorFunc.prototype = Object.create(Error.prototype);\n ctorFunc.prototype.constructor = ctorFunc;\n return ctorFunc;\n}\n", "import { createErrorClass } from './createErrorClass';\n\nexport interface UnsubscriptionError extends Error {\n readonly errors: any[];\n}\n\nexport interface UnsubscriptionErrorCtor {\n /**\n * @deprecated Internal implementation detail. Do not construct error instances.\n * Cannot be tagged as internal: https://github.com/ReactiveX/rxjs/issues/6269\n */\n new (errors: any[]): UnsubscriptionError;\n}\n\n/**\n * An error thrown when one or more errors have occurred during the\n * `unsubscribe` of a {@link Subscription}.\n */\nexport const UnsubscriptionError: UnsubscriptionErrorCtor = createErrorClass(\n (_super) =>\n function UnsubscriptionErrorImpl(this: any, errors: (Error | string)[]) {\n _super(this);\n this.message = errors\n ? `${errors.length} errors occurred during unsubscription:\n${errors.map((err, i) => `${i + 1}) ${err.toString()}`).join('\\n ')}`\n : '';\n this.name = 'UnsubscriptionError';\n this.errors = errors;\n }\n);\n", "/**\n * Removes an item from an array, mutating it.\n * @param arr The array to remove the item from\n * @param item The item to remove\n */\nexport function arrRemove(arr: T[] | undefined | null, item: T) {\n if (arr) {\n const index = arr.indexOf(item);\n 0 <= index && arr.splice(index, 1);\n }\n}\n", "import { isFunction } from './util/isFunction';\nimport { UnsubscriptionError } from './util/UnsubscriptionError';\nimport { SubscriptionLike, TeardownLogic, Unsubscribable } from './types';\nimport { arrRemove } from './util/arrRemove';\n\n/**\n * Represents a disposable resource, such as the execution of an Observable. A\n * Subscription has one important method, `unsubscribe`, that takes no argument\n * and just disposes the resource held by the subscription.\n *\n * Additionally, subscriptions may be grouped together through the `add()`\n * method, which will attach a child Subscription to the current Subscription.\n * When a Subscription is unsubscribed, all its children (and its grandchildren)\n * will be unsubscribed as well.\n *\n * @class Subscription\n */\nexport class Subscription implements SubscriptionLike {\n /** @nocollapse */\n public static EMPTY = (() => {\n const empty = new Subscription();\n empty.closed = true;\n return empty;\n })();\n\n /**\n * A flag to indicate whether this Subscription has already been unsubscribed.\n */\n public closed = false;\n\n private _parentage: Subscription[] | Subscription | null = null;\n\n /**\n * The list of registered finalizers to execute upon unsubscription. Adding and removing from this\n * list occurs in the {@link #add} and {@link #remove} methods.\n */\n private _finalizers: Exclude[] | null = null;\n\n /**\n * @param initialTeardown A function executed first as part of the finalization\n * process that is kicked off when {@link #unsubscribe} is called.\n */\n constructor(private initialTeardown?: () => void) {}\n\n /**\n * Disposes the resources held by the subscription. May, for instance, cancel\n * an ongoing Observable execution or cancel any other type of work that\n * started when the Subscription was created.\n * @return {void}\n */\n unsubscribe(): void {\n let errors: any[] | undefined;\n\n if (!this.closed) {\n this.closed = true;\n\n // Remove this from it's parents.\n const { _parentage } = this;\n if (_parentage) {\n this._parentage = null;\n if (Array.isArray(_parentage)) {\n for (const parent of _parentage) {\n parent.remove(this);\n }\n } else {\n _parentage.remove(this);\n }\n }\n\n const { initialTeardown: initialFinalizer } = this;\n if (isFunction(initialFinalizer)) {\n try {\n initialFinalizer();\n } catch (e) {\n errors = e instanceof UnsubscriptionError ? e.errors : [e];\n }\n }\n\n const { _finalizers } = this;\n if (_finalizers) {\n this._finalizers = null;\n for (const finalizer of _finalizers) {\n try {\n execFinalizer(finalizer);\n } catch (err) {\n errors = errors ?? [];\n if (err instanceof UnsubscriptionError) {\n errors = [...errors, ...err.errors];\n } else {\n errors.push(err);\n }\n }\n }\n }\n\n if (errors) {\n throw new UnsubscriptionError(errors);\n }\n }\n }\n\n /**\n * Adds a finalizer to this subscription, so that finalization will be unsubscribed/called\n * when this subscription is unsubscribed. If this subscription is already {@link #closed},\n * because it has already been unsubscribed, then whatever finalizer is passed to it\n * will automatically be executed (unless the finalizer itself is also a closed subscription).\n *\n * Closed Subscriptions cannot be added as finalizers to any subscription. Adding a closed\n * subscription to a any subscription will result in no operation. (A noop).\n *\n * Adding a subscription to itself, or adding `null` or `undefined` will not perform any\n * operation at all. (A noop).\n *\n * `Subscription` instances that are added to this instance will automatically remove themselves\n * if they are unsubscribed. Functions and {@link Unsubscribable} objects that you wish to remove\n * will need to be removed manually with {@link #remove}\n *\n * @param teardown The finalization logic to add to this subscription.\n */\n add(teardown: TeardownLogic): void {\n // Only add the finalizer if it's not undefined\n // and don't add a subscription to itself.\n if (teardown && teardown !== this) {\n if (this.closed) {\n // If this subscription is already closed,\n // execute whatever finalizer is handed to it automatically.\n execFinalizer(teardown);\n } else {\n if (teardown instanceof Subscription) {\n // We don't add closed subscriptions, and we don't add the same subscription\n // twice. Subscription unsubscribe is idempotent.\n if (teardown.closed || teardown._hasParent(this)) {\n return;\n }\n teardown._addParent(this);\n }\n (this._finalizers = this._finalizers ?? []).push(teardown);\n }\n }\n }\n\n /**\n * Checks to see if a this subscription already has a particular parent.\n * This will signal that this subscription has already been added to the parent in question.\n * @param parent the parent to check for\n */\n private _hasParent(parent: Subscription) {\n const { _parentage } = this;\n return _parentage === parent || (Array.isArray(_parentage) && _parentage.includes(parent));\n }\n\n /**\n * Adds a parent to this subscription so it can be removed from the parent if it\n * unsubscribes on it's own.\n *\n * NOTE: THIS ASSUMES THAT {@link _hasParent} HAS ALREADY BEEN CHECKED.\n * @param parent The parent subscription to add\n */\n private _addParent(parent: Subscription) {\n const { _parentage } = this;\n this._parentage = Array.isArray(_parentage) ? (_parentage.push(parent), _parentage) : _parentage ? [_parentage, parent] : parent;\n }\n\n /**\n * Called on a child when it is removed via {@link #remove}.\n * @param parent The parent to remove\n */\n private _removeParent(parent: Subscription) {\n const { _parentage } = this;\n if (_parentage === parent) {\n this._parentage = null;\n } else if (Array.isArray(_parentage)) {\n arrRemove(_parentage, parent);\n }\n }\n\n /**\n * Removes a finalizer from this subscription that was previously added with the {@link #add} method.\n *\n * Note that `Subscription` instances, when unsubscribed, will automatically remove themselves\n * from every other `Subscription` they have been added to. This means that using the `remove` method\n * is not a common thing and should be used thoughtfully.\n *\n * If you add the same finalizer instance of a function or an unsubscribable object to a `Subscription` instance\n * more than once, you will need to call `remove` the same number of times to remove all instances.\n *\n * All finalizer instances are removed to free up memory upon unsubscription.\n *\n * @param teardown The finalizer to remove from this subscription\n */\n remove(teardown: Exclude): void {\n const { _finalizers } = this;\n _finalizers && arrRemove(_finalizers, teardown);\n\n if (teardown instanceof Subscription) {\n teardown._removeParent(this);\n }\n }\n}\n\nexport const EMPTY_SUBSCRIPTION = Subscription.EMPTY;\n\nexport function isSubscription(value: any): value is Subscription {\n return (\n value instanceof Subscription ||\n (value && 'closed' in value && isFunction(value.remove) && isFunction(value.add) && isFunction(value.unsubscribe))\n );\n}\n\nfunction execFinalizer(finalizer: Unsubscribable | (() => void)) {\n if (isFunction(finalizer)) {\n finalizer();\n } else {\n finalizer.unsubscribe();\n }\n}\n", "import { Subscriber } from './Subscriber';\nimport { ObservableNotification } from './types';\n\n/**\n * The {@link GlobalConfig} object for RxJS. It is used to configure things\n * like how to react on unhandled errors.\n */\nexport const config: GlobalConfig = {\n onUnhandledError: null,\n onStoppedNotification: null,\n Promise: undefined,\n useDeprecatedSynchronousErrorHandling: false,\n useDeprecatedNextContext: false,\n};\n\n/**\n * The global configuration object for RxJS, used to configure things\n * like how to react on unhandled errors. Accessible via {@link config}\n * object.\n */\nexport interface GlobalConfig {\n /**\n * A registration point for unhandled errors from RxJS. These are errors that\n * cannot were not handled by consuming code in the usual subscription path. For\n * example, if you have this configured, and you subscribe to an observable without\n * providing an error handler, errors from that subscription will end up here. This\n * will _always_ be called asynchronously on another job in the runtime. This is because\n * we do not want errors thrown in this user-configured handler to interfere with the\n * behavior of the library.\n */\n onUnhandledError: ((err: any) => void) | null;\n\n /**\n * A registration point for notifications that cannot be sent to subscribers because they\n * have completed, errored or have been explicitly unsubscribed. By default, next, complete\n * and error notifications sent to stopped subscribers are noops. However, sometimes callers\n * might want a different behavior. For example, with sources that attempt to report errors\n * to stopped subscribers, a caller can configure RxJS to throw an unhandled error instead.\n * This will _always_ be called asynchronously on another job in the runtime. This is because\n * we do not want errors thrown in this user-configured handler to interfere with the\n * behavior of the library.\n */\n onStoppedNotification: ((notification: ObservableNotification, subscriber: Subscriber) => void) | null;\n\n /**\n * The promise constructor used by default for {@link Observable#toPromise toPromise} and {@link Observable#forEach forEach}\n * methods.\n *\n * @deprecated As of version 8, RxJS will no longer support this sort of injection of a\n * Promise constructor. If you need a Promise implementation other than native promises,\n * please polyfill/patch Promise as you see appropriate. Will be removed in v8.\n */\n Promise?: PromiseConstructorLike;\n\n /**\n * If true, turns on synchronous error rethrowing, which is a deprecated behavior\n * in v6 and higher. This behavior enables bad patterns like wrapping a subscribe\n * call in a try/catch block. It also enables producer interference, a nasty bug\n * where a multicast can be broken for all observers by a downstream consumer with\n * an unhandled error. DO NOT USE THIS FLAG UNLESS IT'S NEEDED TO BUY TIME\n * FOR MIGRATION REASONS.\n *\n * @deprecated As of version 8, RxJS will no longer support synchronous throwing\n * of unhandled errors. All errors will be thrown on a separate call stack to prevent bad\n * behaviors described above. Will be removed in v8.\n */\n useDeprecatedSynchronousErrorHandling: boolean;\n\n /**\n * If true, enables an as-of-yet undocumented feature from v5: The ability to access\n * `unsubscribe()` via `this` context in `next` functions created in observers passed\n * to `subscribe`.\n *\n * This is being removed because the performance was severely problematic, and it could also cause\n * issues when types other than POJOs are passed to subscribe as subscribers, as they will likely have\n * their `this` context overwritten.\n *\n * @deprecated As of version 8, RxJS will no longer support altering the\n * context of next functions provided as part of an observer to Subscribe. Instead,\n * you will have access to a subscription or a signal or token that will allow you to do things like\n * unsubscribe and test closed status. Will be removed in v8.\n */\n useDeprecatedNextContext: boolean;\n}\n", "import type { TimerHandle } from './timerHandle';\ntype SetTimeoutFunction = (handler: () => void, timeout?: number, ...args: any[]) => TimerHandle;\ntype ClearTimeoutFunction = (handle: TimerHandle) => void;\n\ninterface TimeoutProvider {\n setTimeout: SetTimeoutFunction;\n clearTimeout: ClearTimeoutFunction;\n delegate:\n | {\n setTimeout: SetTimeoutFunction;\n clearTimeout: ClearTimeoutFunction;\n }\n | undefined;\n}\n\nexport const timeoutProvider: TimeoutProvider = {\n // When accessing the delegate, use the variable rather than `this` so that\n // the functions can be called without being bound to the provider.\n setTimeout(handler: () => void, timeout?: number, ...args) {\n const { delegate } = timeoutProvider;\n if (delegate?.setTimeout) {\n return delegate.setTimeout(handler, timeout, ...args);\n }\n return setTimeout(handler, timeout, ...args);\n },\n clearTimeout(handle) {\n const { delegate } = timeoutProvider;\n return (delegate?.clearTimeout || clearTimeout)(handle as any);\n },\n delegate: undefined,\n};\n", "import { config } from '../config';\nimport { timeoutProvider } from '../scheduler/timeoutProvider';\n\n/**\n * Handles an error on another job either with the user-configured {@link onUnhandledError},\n * or by throwing it on that new job so it can be picked up by `window.onerror`, `process.on('error')`, etc.\n *\n * This should be called whenever there is an error that is out-of-band with the subscription\n * or when an error hits a terminal boundary of the subscription and no error handler was provided.\n *\n * @param err the error to report\n */\nexport function reportUnhandledError(err: any) {\n timeoutProvider.setTimeout(() => {\n const { onUnhandledError } = config;\n if (onUnhandledError) {\n // Execute the user-configured error handler.\n onUnhandledError(err);\n } else {\n // Throw so it is picked up by the runtime's uncaught error mechanism.\n throw err;\n }\n });\n}\n", "/* tslint:disable:no-empty */\nexport function noop() { }\n", "import { CompleteNotification, NextNotification, ErrorNotification } from './types';\n\n/**\n * A completion object optimized for memory use and created to be the\n * same \"shape\" as other notifications in v8.\n * @internal\n */\nexport const COMPLETE_NOTIFICATION = (() => createNotification('C', undefined, undefined) as CompleteNotification)();\n\n/**\n * Internal use only. Creates an optimized error notification that is the same \"shape\"\n * as other notifications.\n * @internal\n */\nexport function errorNotification(error: any): ErrorNotification {\n return createNotification('E', undefined, error) as any;\n}\n\n/**\n * Internal use only. Creates an optimized next notification that is the same \"shape\"\n * as other notifications.\n * @internal\n */\nexport function nextNotification(value: T) {\n return createNotification('N', value, undefined) as NextNotification;\n}\n\n/**\n * Ensures that all notifications created internally have the same \"shape\" in v8.\n *\n * TODO: This is only exported to support a crazy legacy test in `groupBy`.\n * @internal\n */\nexport function createNotification(kind: 'N' | 'E' | 'C', value: any, error: any) {\n return {\n kind,\n value,\n error,\n };\n}\n", "import { config } from '../config';\n\nlet context: { errorThrown: boolean; error: any } | null = null;\n\n/**\n * Handles dealing with errors for super-gross mode. Creates a context, in which\n * any synchronously thrown errors will be passed to {@link captureError}. Which\n * will record the error such that it will be rethrown after the call back is complete.\n * TODO: Remove in v8\n * @param cb An immediately executed function.\n */\nexport function errorContext(cb: () => void) {\n if (config.useDeprecatedSynchronousErrorHandling) {\n const isRoot = !context;\n if (isRoot) {\n context = { errorThrown: false, error: null };\n }\n cb();\n if (isRoot) {\n const { errorThrown, error } = context!;\n context = null;\n if (errorThrown) {\n throw error;\n }\n }\n } else {\n // This is the general non-deprecated path for everyone that\n // isn't crazy enough to use super-gross mode (useDeprecatedSynchronousErrorHandling)\n cb();\n }\n}\n\n/**\n * Captures errors only in super-gross mode.\n * @param err the error to capture\n */\nexport function captureError(err: any) {\n if (config.useDeprecatedSynchronousErrorHandling && context) {\n context.errorThrown = true;\n context.error = err;\n }\n}\n", "import { isFunction } from './util/isFunction';\nimport { Observer, ObservableNotification } from './types';\nimport { isSubscription, Subscription } from './Subscription';\nimport { config } from './config';\nimport { reportUnhandledError } from './util/reportUnhandledError';\nimport { noop } from './util/noop';\nimport { nextNotification, errorNotification, COMPLETE_NOTIFICATION } from './NotificationFactories';\nimport { timeoutProvider } from './scheduler/timeoutProvider';\nimport { captureError } from './util/errorContext';\n\n/**\n * Implements the {@link Observer} interface and extends the\n * {@link Subscription} class. While the {@link Observer} is the public API for\n * consuming the values of an {@link Observable}, all Observers get converted to\n * a Subscriber, in order to provide Subscription-like capabilities such as\n * `unsubscribe`. Subscriber is a common type in RxJS, and crucial for\n * implementing operators, but it is rarely used as a public API.\n *\n * @class Subscriber\n */\nexport class Subscriber extends Subscription implements Observer {\n /**\n * A static factory for a Subscriber, given a (potentially partial) definition\n * of an Observer.\n * @param next The `next` callback of an Observer.\n * @param error The `error` callback of an\n * Observer.\n * @param complete The `complete` callback of an\n * Observer.\n * @return A Subscriber wrapping the (partially defined)\n * Observer represented by the given arguments.\n * @nocollapse\n * @deprecated Do not use. Will be removed in v8. There is no replacement for this\n * method, and there is no reason to be creating instances of `Subscriber` directly.\n * If you have a specific use case, please file an issue.\n */\n static create(next?: (x?: T) => void, error?: (e?: any) => void, complete?: () => void): Subscriber {\n return new SafeSubscriber(next, error, complete);\n }\n\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n protected isStopped: boolean = false;\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n protected destination: Subscriber | Observer; // this `any` is the escape hatch to erase extra type param (e.g. R)\n\n /**\n * @deprecated Internal implementation detail, do not use directly. Will be made internal in v8.\n * There is no reason to directly create an instance of Subscriber. This type is exported for typings reasons.\n */\n constructor(destination?: Subscriber | Observer) {\n super();\n if (destination) {\n this.destination = destination;\n // Automatically chain subscriptions together here.\n // if destination is a Subscription, then it is a Subscriber.\n if (isSubscription(destination)) {\n destination.add(this);\n }\n } else {\n this.destination = EMPTY_OBSERVER;\n }\n }\n\n /**\n * The {@link Observer} callback to receive notifications of type `next` from\n * the Observable, with a value. The Observable may call this method 0 or more\n * times.\n * @param {T} [value] The `next` value.\n * @return {void}\n */\n next(value?: T): void {\n if (this.isStopped) {\n handleStoppedNotification(nextNotification(value), this);\n } else {\n this._next(value!);\n }\n }\n\n /**\n * The {@link Observer} callback to receive notifications of type `error` from\n * the Observable, with an attached `Error`. Notifies the Observer that\n * the Observable has experienced an error condition.\n * @param {any} [err] The `error` exception.\n * @return {void}\n */\n error(err?: any): void {\n if (this.isStopped) {\n handleStoppedNotification(errorNotification(err), this);\n } else {\n this.isStopped = true;\n this._error(err);\n }\n }\n\n /**\n * The {@link Observer} callback to receive a valueless notification of type\n * `complete` from the Observable. Notifies the Observer that the Observable\n * has finished sending push-based notifications.\n * @return {void}\n */\n complete(): void {\n if (this.isStopped) {\n handleStoppedNotification(COMPLETE_NOTIFICATION, this);\n } else {\n this.isStopped = true;\n this._complete();\n }\n }\n\n unsubscribe(): void {\n if (!this.closed) {\n this.isStopped = true;\n super.unsubscribe();\n this.destination = null!;\n }\n }\n\n protected _next(value: T): void {\n this.destination.next(value);\n }\n\n protected _error(err: any): void {\n try {\n this.destination.error(err);\n } finally {\n this.unsubscribe();\n }\n }\n\n protected _complete(): void {\n try {\n this.destination.complete();\n } finally {\n this.unsubscribe();\n }\n }\n}\n\n/**\n * This bind is captured here because we want to be able to have\n * compatibility with monoid libraries that tend to use a method named\n * `bind`. In particular, a library called Monio requires this.\n */\nconst _bind = Function.prototype.bind;\n\nfunction bind any>(fn: Fn, thisArg: any): Fn {\n return _bind.call(fn, thisArg);\n}\n\n/**\n * Internal optimization only, DO NOT EXPOSE.\n * @internal\n */\nclass ConsumerObserver implements Observer {\n constructor(private partialObserver: Partial>) {}\n\n next(value: T): void {\n const { partialObserver } = this;\n if (partialObserver.next) {\n try {\n partialObserver.next(value);\n } catch (error) {\n handleUnhandledError(error);\n }\n }\n }\n\n error(err: any): void {\n const { partialObserver } = this;\n if (partialObserver.error) {\n try {\n partialObserver.error(err);\n } catch (error) {\n handleUnhandledError(error);\n }\n } else {\n handleUnhandledError(err);\n }\n }\n\n complete(): void {\n const { partialObserver } = this;\n if (partialObserver.complete) {\n try {\n partialObserver.complete();\n } catch (error) {\n handleUnhandledError(error);\n }\n }\n }\n}\n\nexport class SafeSubscriber extends Subscriber {\n constructor(\n observerOrNext?: Partial> | ((value: T) => void) | null,\n error?: ((e?: any) => void) | null,\n complete?: (() => void) | null\n ) {\n super();\n\n let partialObserver: Partial>;\n if (isFunction(observerOrNext) || !observerOrNext) {\n // The first argument is a function, not an observer. The next\n // two arguments *could* be observers, or they could be empty.\n partialObserver = {\n next: (observerOrNext ?? undefined) as (((value: T) => void) | undefined),\n error: error ?? undefined,\n complete: complete ?? undefined,\n };\n } else {\n // The first argument is a partial observer.\n let context: any;\n if (this && config.useDeprecatedNextContext) {\n // This is a deprecated path that made `this.unsubscribe()` available in\n // next handler functions passed to subscribe. This only exists behind a flag\n // now, as it is *very* slow.\n context = Object.create(observerOrNext);\n context.unsubscribe = () => this.unsubscribe();\n partialObserver = {\n next: observerOrNext.next && bind(observerOrNext.next, context),\n error: observerOrNext.error && bind(observerOrNext.error, context),\n complete: observerOrNext.complete && bind(observerOrNext.complete, context),\n };\n } else {\n // The \"normal\" path. Just use the partial observer directly.\n partialObserver = observerOrNext;\n }\n }\n\n // Wrap the partial observer to ensure it's a full observer, and\n // make sure proper error handling is accounted for.\n this.destination = new ConsumerObserver(partialObserver);\n }\n}\n\nfunction handleUnhandledError(error: any) {\n if (config.useDeprecatedSynchronousErrorHandling) {\n captureError(error);\n } else {\n // Ideal path, we report this as an unhandled error,\n // which is thrown on a new call stack.\n reportUnhandledError(error);\n }\n}\n\n/**\n * An error handler used when no error handler was supplied\n * to the SafeSubscriber -- meaning no error handler was supplied\n * do the `subscribe` call on our observable.\n * @param err The error to handle\n */\nfunction defaultErrorHandler(err: any) {\n throw err;\n}\n\n/**\n * A handler for notifications that cannot be sent to a stopped subscriber.\n * @param notification The notification being sent\n * @param subscriber The stopped subscriber\n */\nfunction handleStoppedNotification(notification: ObservableNotification, subscriber: Subscriber) {\n const { onStoppedNotification } = config;\n onStoppedNotification && timeoutProvider.setTimeout(() => onStoppedNotification(notification, subscriber));\n}\n\n/**\n * The observer used as a stub for subscriptions where the user did not\n * pass any arguments to `subscribe`. Comes with the default error handling\n * behavior.\n */\nexport const EMPTY_OBSERVER: Readonly> & { closed: true } = {\n closed: true,\n next: noop,\n error: defaultErrorHandler,\n complete: noop,\n};\n", "/**\n * Symbol.observable or a string \"@@observable\". Used for interop\n *\n * @deprecated We will no longer be exporting this symbol in upcoming versions of RxJS.\n * Instead polyfill and use Symbol.observable directly *or* use https://www.npmjs.com/package/symbol-observable\n */\nexport const observable: string | symbol = (() => (typeof Symbol === 'function' && Symbol.observable) || '@@observable')();\n", "/**\n * This function takes one parameter and just returns it. Simply put,\n * this is like `(x: T): T => x`.\n *\n * ## Examples\n *\n * This is useful in some cases when using things like `mergeMap`\n *\n * ```ts\n * import { interval, take, map, range, mergeMap, identity } from 'rxjs';\n *\n * const source$ = interval(1000).pipe(take(5));\n *\n * const result$ = source$.pipe(\n * map(i => range(i)),\n * mergeMap(identity) // same as mergeMap(x => x)\n * );\n *\n * result$.subscribe({\n * next: console.log\n * });\n * ```\n *\n * Or when you want to selectively apply an operator\n *\n * ```ts\n * import { interval, take, identity } from 'rxjs';\n *\n * const shouldLimit = () => Math.random() < 0.5;\n *\n * const source$ = interval(1000);\n *\n * const result$ = source$.pipe(shouldLimit() ? take(5) : identity);\n *\n * result$.subscribe({\n * next: console.log\n * });\n * ```\n *\n * @param x Any value that is returned by this function\n * @returns The value passed as the first parameter to this function\n */\nexport function identity(x: T): T {\n return x;\n}\n", "import { identity } from './identity';\nimport { UnaryFunction } from '../types';\n\nexport function pipe(): typeof identity;\nexport function pipe(fn1: UnaryFunction): UnaryFunction;\nexport function pipe(fn1: UnaryFunction, fn2: UnaryFunction): UnaryFunction;\nexport function pipe(fn1: UnaryFunction, fn2: UnaryFunction, fn3: UnaryFunction): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction,\n fn7: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction,\n fn7: UnaryFunction,\n fn8: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction,\n fn7: UnaryFunction,\n fn8: UnaryFunction,\n fn9: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction,\n fn7: UnaryFunction,\n fn8: UnaryFunction,\n fn9: UnaryFunction,\n ...fns: UnaryFunction[]\n): UnaryFunction;\n\n/**\n * pipe() can be called on one or more functions, each of which can take one argument (\"UnaryFunction\")\n * and uses it to return a value.\n * It returns a function that takes one argument, passes it to the first UnaryFunction, and then\n * passes the result to the next one, passes that result to the next one, and so on. \n */\nexport function pipe(...fns: Array>): UnaryFunction {\n return pipeFromArray(fns);\n}\n\n/** @internal */\nexport function pipeFromArray(fns: Array>): UnaryFunction {\n if (fns.length === 0) {\n return identity as UnaryFunction;\n }\n\n if (fns.length === 1) {\n return fns[0];\n }\n\n return function piped(input: T): R {\n return fns.reduce((prev: any, fn: UnaryFunction) => fn(prev), input as any);\n };\n}\n", "import { Operator } from './Operator';\nimport { SafeSubscriber, Subscriber } from './Subscriber';\nimport { isSubscription, Subscription } from './Subscription';\nimport { TeardownLogic, OperatorFunction, Subscribable, Observer } from './types';\nimport { observable as Symbol_observable } from './symbol/observable';\nimport { pipeFromArray } from './util/pipe';\nimport { config } from './config';\nimport { isFunction } from './util/isFunction';\nimport { errorContext } from './util/errorContext';\n\n/**\n * A representation of any set of values over any amount of time. This is the most basic building block\n * of RxJS.\n *\n * @class Observable\n */\nexport class Observable implements Subscribable {\n /**\n * @deprecated Internal implementation detail, do not use directly. Will be made internal in v8.\n */\n source: Observable | undefined;\n\n /**\n * @deprecated Internal implementation detail, do not use directly. Will be made internal in v8.\n */\n operator: Operator | undefined;\n\n /**\n * @constructor\n * @param {Function} subscribe the function that is called when the Observable is\n * initially subscribed to. This function is given a Subscriber, to which new values\n * can be `next`ed, or an `error` method can be called to raise an error, or\n * `complete` can be called to notify of a successful completion.\n */\n constructor(subscribe?: (this: Observable, subscriber: Subscriber) => TeardownLogic) {\n if (subscribe) {\n this._subscribe = subscribe;\n }\n }\n\n // HACK: Since TypeScript inherits static properties too, we have to\n // fight against TypeScript here so Subject can have a different static create signature\n /**\n * Creates a new Observable by calling the Observable constructor\n * @owner Observable\n * @method create\n * @param {Function} subscribe? the subscriber function to be passed to the Observable constructor\n * @return {Observable} a new observable\n * @nocollapse\n * @deprecated Use `new Observable()` instead. Will be removed in v8.\n */\n static create: (...args: any[]) => any = (subscribe?: (subscriber: Subscriber) => TeardownLogic) => {\n return new Observable(subscribe);\n };\n\n /**\n * Creates a new Observable, with this Observable instance as the source, and the passed\n * operator defined as the new observable's operator.\n * @method lift\n * @param operator the operator defining the operation to take on the observable\n * @return a new observable with the Operator applied\n * @deprecated Internal implementation detail, do not use directly. Will be made internal in v8.\n * If you have implemented an operator using `lift`, it is recommended that you create an\n * operator by simply returning `new Observable()` directly. See \"Creating new operators from\n * scratch\" section here: https://rxjs.dev/guide/operators\n */\n lift(operator?: Operator): Observable {\n const observable = new Observable();\n observable.source = this;\n observable.operator = operator;\n return observable;\n }\n\n subscribe(observerOrNext?: Partial> | ((value: T) => void)): Subscription;\n /** @deprecated Instead of passing separate callback arguments, use an observer argument. Signatures taking separate callback arguments will be removed in v8. Details: https://rxjs.dev/deprecations/subscribe-arguments */\n subscribe(next?: ((value: T) => void) | null, error?: ((error: any) => void) | null, complete?: (() => void) | null): Subscription;\n /**\n * Invokes an execution of an Observable and registers Observer handlers for notifications it will emit.\n *\n * Use it when you have all these Observables, but still nothing is happening.\n *\n * `subscribe` is not a regular operator, but a method that calls Observable's internal `subscribe` function. It\n * might be for example a function that you passed to Observable's constructor, but most of the time it is\n * a library implementation, which defines what will be emitted by an Observable, and when it be will emitted. This means\n * that calling `subscribe` is actually the moment when Observable starts its work, not when it is created, as it is often\n * the thought.\n *\n * Apart from starting the execution of an Observable, this method allows you to listen for values\n * that an Observable emits, as well as for when it completes or errors. You can achieve this in two\n * of the following ways.\n *\n * The first way is creating an object that implements {@link Observer} interface. It should have methods\n * defined by that interface, but note that it should be just a regular JavaScript object, which you can create\n * yourself in any way you want (ES6 class, classic function constructor, object literal etc.). In particular, do\n * not attempt to use any RxJS implementation details to create Observers - you don't need them. Remember also\n * that your object does not have to implement all methods. If you find yourself creating a method that doesn't\n * do anything, you can simply omit it. Note however, if the `error` method is not provided and an error happens,\n * it will be thrown asynchronously. Errors thrown asynchronously cannot be caught using `try`/`catch`. Instead,\n * use the {@link onUnhandledError} configuration option or use a runtime handler (like `window.onerror` or\n * `process.on('error)`) to be notified of unhandled errors. Because of this, it's recommended that you provide\n * an `error` method to avoid missing thrown errors.\n *\n * The second way is to give up on Observer object altogether and simply provide callback functions in place of its methods.\n * This means you can provide three functions as arguments to `subscribe`, where the first function is equivalent\n * of a `next` method, the second of an `error` method and the third of a `complete` method. Just as in case of an Observer,\n * if you do not need to listen for something, you can omit a function by passing `undefined` or `null`,\n * since `subscribe` recognizes these functions by where they were placed in function call. When it comes\n * to the `error` function, as with an Observer, if not provided, errors emitted by an Observable will be thrown asynchronously.\n *\n * You can, however, subscribe with no parameters at all. This may be the case where you're not interested in terminal events\n * and you also handled emissions internally by using operators (e.g. using `tap`).\n *\n * Whichever style of calling `subscribe` you use, in both cases it returns a Subscription object.\n * This object allows you to call `unsubscribe` on it, which in turn will stop the work that an Observable does and will clean\n * up all resources that an Observable used. Note that cancelling a subscription will not call `complete` callback\n * provided to `subscribe` function, which is reserved for a regular completion signal that comes from an Observable.\n *\n * Remember that callbacks provided to `subscribe` are not guaranteed to be called asynchronously.\n * It is an Observable itself that decides when these functions will be called. For example {@link of}\n * by default emits all its values synchronously. Always check documentation for how given Observable\n * will behave when subscribed and if its default behavior can be modified with a `scheduler`.\n *\n * #### Examples\n *\n * Subscribe with an {@link guide/observer Observer}\n *\n * ```ts\n * import { of } from 'rxjs';\n *\n * const sumObserver = {\n * sum: 0,\n * next(value) {\n * console.log('Adding: ' + value);\n * this.sum = this.sum + value;\n * },\n * error() {\n * // We actually could just remove this method,\n * // since we do not really care about errors right now.\n * },\n * complete() {\n * console.log('Sum equals: ' + this.sum);\n * }\n * };\n *\n * of(1, 2, 3) // Synchronously emits 1, 2, 3 and then completes.\n * .subscribe(sumObserver);\n *\n * // Logs:\n * // 'Adding: 1'\n * // 'Adding: 2'\n * // 'Adding: 3'\n * // 'Sum equals: 6'\n * ```\n *\n * Subscribe with functions ({@link deprecations/subscribe-arguments deprecated})\n *\n * ```ts\n * import { of } from 'rxjs'\n *\n * let sum = 0;\n *\n * of(1, 2, 3).subscribe(\n * value => {\n * console.log('Adding: ' + value);\n * sum = sum + value;\n * },\n * undefined,\n * () => console.log('Sum equals: ' + sum)\n * );\n *\n * // Logs:\n * // 'Adding: 1'\n * // 'Adding: 2'\n * // 'Adding: 3'\n * // 'Sum equals: 6'\n * ```\n *\n * Cancel a subscription\n *\n * ```ts\n * import { interval } from 'rxjs';\n *\n * const subscription = interval(1000).subscribe({\n * next(num) {\n * console.log(num)\n * },\n * complete() {\n * // Will not be called, even when cancelling subscription.\n * console.log('completed!');\n * }\n * });\n *\n * setTimeout(() => {\n * subscription.unsubscribe();\n * console.log('unsubscribed!');\n * }, 2500);\n *\n * // Logs:\n * // 0 after 1s\n * // 1 after 2s\n * // 'unsubscribed!' after 2.5s\n * ```\n *\n * @param {Observer|Function} observerOrNext (optional) Either an observer with methods to be called,\n * or the first of three possible handlers, which is the handler for each value emitted from the subscribed\n * Observable.\n * @param {Function} error (optional) A handler for a terminal event resulting from an error. If no error handler is provided,\n * the error will be thrown asynchronously as unhandled.\n * @param {Function} complete (optional) A handler for a terminal event resulting from successful completion.\n * @return {Subscription} a subscription reference to the registered handlers\n * @method subscribe\n */\n subscribe(\n observerOrNext?: Partial> | ((value: T) => void) | null,\n error?: ((error: any) => void) | null,\n complete?: (() => void) | null\n ): Subscription {\n const subscriber = isSubscriber(observerOrNext) ? observerOrNext : new SafeSubscriber(observerOrNext, error, complete);\n\n errorContext(() => {\n const { operator, source } = this;\n subscriber.add(\n operator\n ? // We're dealing with a subscription in the\n // operator chain to one of our lifted operators.\n operator.call(subscriber, source)\n : source\n ? // If `source` has a value, but `operator` does not, something that\n // had intimate knowledge of our API, like our `Subject`, must have\n // set it. We're going to just call `_subscribe` directly.\n this._subscribe(subscriber)\n : // In all other cases, we're likely wrapping a user-provided initializer\n // function, so we need to catch errors and handle them appropriately.\n this._trySubscribe(subscriber)\n );\n });\n\n return subscriber;\n }\n\n /** @internal */\n protected _trySubscribe(sink: Subscriber): TeardownLogic {\n try {\n return this._subscribe(sink);\n } catch (err) {\n // We don't need to return anything in this case,\n // because it's just going to try to `add()` to a subscription\n // above.\n sink.error(err);\n }\n }\n\n /**\n * Used as a NON-CANCELLABLE means of subscribing to an observable, for use with\n * APIs that expect promises, like `async/await`. You cannot unsubscribe from this.\n *\n * **WARNING**: Only use this with observables you *know* will complete. If the source\n * observable does not complete, you will end up with a promise that is hung up, and\n * potentially all of the state of an async function hanging out in memory. To avoid\n * this situation, look into adding something like {@link timeout}, {@link take},\n * {@link takeWhile}, or {@link takeUntil} amongst others.\n *\n * #### Example\n *\n * ```ts\n * import { interval, take } from 'rxjs';\n *\n * const source$ = interval(1000).pipe(take(4));\n *\n * async function getTotal() {\n * let total = 0;\n *\n * await source$.forEach(value => {\n * total += value;\n * console.log('observable -> ' + value);\n * });\n *\n * return total;\n * }\n *\n * getTotal().then(\n * total => console.log('Total: ' + total)\n * );\n *\n * // Expected:\n * // 'observable -> 0'\n * // 'observable -> 1'\n * // 'observable -> 2'\n * // 'observable -> 3'\n * // 'Total: 6'\n * ```\n *\n * @param next a handler for each value emitted by the observable\n * @return a promise that either resolves on observable completion or\n * rejects with the handled error\n */\n forEach(next: (value: T) => void): Promise;\n\n /**\n * @param next a handler for each value emitted by the observable\n * @param promiseCtor a constructor function used to instantiate the Promise\n * @return a promise that either resolves on observable completion or\n * rejects with the handled error\n * @deprecated Passing a Promise constructor will no longer be available\n * in upcoming versions of RxJS. This is because it adds weight to the library, for very\n * little benefit. If you need this functionality, it is recommended that you either\n * polyfill Promise, or you create an adapter to convert the returned native promise\n * to whatever promise implementation you wanted. Will be removed in v8.\n */\n forEach(next: (value: T) => void, promiseCtor: PromiseConstructorLike): Promise;\n\n forEach(next: (value: T) => void, promiseCtor?: PromiseConstructorLike): Promise {\n promiseCtor = getPromiseCtor(promiseCtor);\n\n return new promiseCtor((resolve, reject) => {\n const subscriber = new SafeSubscriber({\n next: (value) => {\n try {\n next(value);\n } catch (err) {\n reject(err);\n subscriber.unsubscribe();\n }\n },\n error: reject,\n complete: resolve,\n });\n this.subscribe(subscriber);\n }) as Promise;\n }\n\n /** @internal */\n protected _subscribe(subscriber: Subscriber): TeardownLogic {\n return this.source?.subscribe(subscriber);\n }\n\n /**\n * An interop point defined by the es7-observable spec https://github.com/zenparsing/es-observable\n * @method Symbol.observable\n * @return {Observable} this instance of the observable\n */\n [Symbol_observable]() {\n return this;\n }\n\n /* tslint:disable:max-line-length */\n pipe(): Observable;\n pipe(op1: OperatorFunction): Observable;\n pipe(op1: OperatorFunction, op2: OperatorFunction): Observable;\n pipe(op1: OperatorFunction, op2: OperatorFunction, op3: OperatorFunction): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction,\n op7: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction,\n op7: OperatorFunction,\n op8: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction,\n op7: OperatorFunction,\n op8: OperatorFunction,\n op9: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction,\n op7: OperatorFunction,\n op8: OperatorFunction,\n op9: OperatorFunction,\n ...operations: OperatorFunction[]\n ): Observable;\n /* tslint:enable:max-line-length */\n\n /**\n * Used to stitch together functional operators into a chain.\n * @method pipe\n * @return {Observable} the Observable result of all of the operators having\n * been called in the order they were passed in.\n *\n * ## Example\n *\n * ```ts\n * import { interval, filter, map, scan } from 'rxjs';\n *\n * interval(1000)\n * .pipe(\n * filter(x => x % 2 === 0),\n * map(x => x + x),\n * scan((acc, x) => acc + x)\n * )\n * .subscribe(x => console.log(x));\n * ```\n */\n pipe(...operations: OperatorFunction[]): Observable {\n return pipeFromArray(operations)(this);\n }\n\n /* tslint:disable:max-line-length */\n /** @deprecated Replaced with {@link firstValueFrom} and {@link lastValueFrom}. Will be removed in v8. Details: https://rxjs.dev/deprecations/to-promise */\n toPromise(): Promise;\n /** @deprecated Replaced with {@link firstValueFrom} and {@link lastValueFrom}. Will be removed in v8. Details: https://rxjs.dev/deprecations/to-promise */\n toPromise(PromiseCtor: typeof Promise): Promise;\n /** @deprecated Replaced with {@link firstValueFrom} and {@link lastValueFrom}. Will be removed in v8. Details: https://rxjs.dev/deprecations/to-promise */\n toPromise(PromiseCtor: PromiseConstructorLike): Promise;\n /* tslint:enable:max-line-length */\n\n /**\n * Subscribe to this Observable and get a Promise resolving on\n * `complete` with the last emission (if any).\n *\n * **WARNING**: Only use this with observables you *know* will complete. If the source\n * observable does not complete, you will end up with a promise that is hung up, and\n * potentially all of the state of an async function hanging out in memory. To avoid\n * this situation, look into adding something like {@link timeout}, {@link take},\n * {@link takeWhile}, or {@link takeUntil} amongst others.\n *\n * @method toPromise\n * @param [promiseCtor] a constructor function used to instantiate\n * the Promise\n * @return A Promise that resolves with the last value emit, or\n * rejects on an error. If there were no emissions, Promise\n * resolves with undefined.\n * @deprecated Replaced with {@link firstValueFrom} and {@link lastValueFrom}. Will be removed in v8. Details: https://rxjs.dev/deprecations/to-promise\n */\n toPromise(promiseCtor?: PromiseConstructorLike): Promise {\n promiseCtor = getPromiseCtor(promiseCtor);\n\n return new promiseCtor((resolve, reject) => {\n let value: T | undefined;\n this.subscribe(\n (x: T) => (value = x),\n (err: any) => reject(err),\n () => resolve(value)\n );\n }) as Promise;\n }\n}\n\n/**\n * Decides between a passed promise constructor from consuming code,\n * A default configured promise constructor, and the native promise\n * constructor and returns it. If nothing can be found, it will throw\n * an error.\n * @param promiseCtor The optional promise constructor to passed by consuming code\n */\nfunction getPromiseCtor(promiseCtor: PromiseConstructorLike | undefined) {\n return promiseCtor ?? config.Promise ?? Promise;\n}\n\nfunction isObserver(value: any): value is Observer {\n return value && isFunction(value.next) && isFunction(value.error) && isFunction(value.complete);\n}\n\nfunction isSubscriber(value: any): value is Subscriber {\n return (value && value instanceof Subscriber) || (isObserver(value) && isSubscription(value));\n}\n", "import { Observable } from '../Observable';\nimport { Subscriber } from '../Subscriber';\nimport { OperatorFunction } from '../types';\nimport { isFunction } from './isFunction';\n\n/**\n * Used to determine if an object is an Observable with a lift function.\n */\nexport function hasLift(source: any): source is { lift: InstanceType['lift'] } {\n return isFunction(source?.lift);\n}\n\n/**\n * Creates an `OperatorFunction`. Used to define operators throughout the library in a concise way.\n * @param init The logic to connect the liftedSource to the subscriber at the moment of subscription.\n */\nexport function operate(\n init: (liftedSource: Observable, subscriber: Subscriber) => (() => void) | void\n): OperatorFunction {\n return (source: Observable) => {\n if (hasLift(source)) {\n return source.lift(function (this: Subscriber, liftedSource: Observable) {\n try {\n return init(liftedSource, this);\n } catch (err) {\n this.error(err);\n }\n });\n }\n throw new TypeError('Unable to lift unknown Observable type');\n };\n}\n", "import { Subscriber } from '../Subscriber';\n\n/**\n * Creates an instance of an `OperatorSubscriber`.\n * @param destination The downstream subscriber.\n * @param onNext Handles next values, only called if this subscriber is not stopped or closed. Any\n * error that occurs in this function is caught and sent to the `error` method of this subscriber.\n * @param onError Handles errors from the subscription, any errors that occur in this handler are caught\n * and send to the `destination` error handler.\n * @param onComplete Handles completion notification from the subscription. Any errors that occur in\n * this handler are sent to the `destination` error handler.\n * @param onFinalize Additional teardown logic here. This will only be called on teardown if the\n * subscriber itself is not already closed. This is called after all other teardown logic is executed.\n */\nexport function createOperatorSubscriber(\n destination: Subscriber,\n onNext?: (value: T) => void,\n onComplete?: () => void,\n onError?: (err: any) => void,\n onFinalize?: () => void\n): Subscriber {\n return new OperatorSubscriber(destination, onNext, onComplete, onError, onFinalize);\n}\n\n/**\n * A generic helper for allowing operators to be created with a Subscriber and\n * use closures to capture necessary state from the operator function itself.\n */\nexport class OperatorSubscriber extends Subscriber {\n /**\n * Creates an instance of an `OperatorSubscriber`.\n * @param destination The downstream subscriber.\n * @param onNext Handles next values, only called if this subscriber is not stopped or closed. Any\n * error that occurs in this function is caught and sent to the `error` method of this subscriber.\n * @param onError Handles errors from the subscription, any errors that occur in this handler are caught\n * and send to the `destination` error handler.\n * @param onComplete Handles completion notification from the subscription. Any errors that occur in\n * this handler are sent to the `destination` error handler.\n * @param onFinalize Additional finalization logic here. This will only be called on finalization if the\n * subscriber itself is not already closed. This is called after all other finalization logic is executed.\n * @param shouldUnsubscribe An optional check to see if an unsubscribe call should truly unsubscribe.\n * NOTE: This currently **ONLY** exists to support the strange behavior of {@link groupBy}, where unsubscription\n * to the resulting observable does not actually disconnect from the source if there are active subscriptions\n * to any grouped observable. (DO NOT EXPOSE OR USE EXTERNALLY!!!)\n */\n constructor(\n destination: Subscriber,\n onNext?: (value: T) => void,\n onComplete?: () => void,\n onError?: (err: any) => void,\n private onFinalize?: () => void,\n private shouldUnsubscribe?: () => boolean\n ) {\n // It's important - for performance reasons - that all of this class's\n // members are initialized and that they are always initialized in the same\n // order. This will ensure that all OperatorSubscriber instances have the\n // same hidden class in V8. This, in turn, will help keep the number of\n // hidden classes involved in property accesses within the base class as\n // low as possible. If the number of hidden classes involved exceeds four,\n // the property accesses will become megamorphic and performance penalties\n // will be incurred - i.e. inline caches won't be used.\n //\n // The reasons for ensuring all instances have the same hidden class are\n // further discussed in this blog post from Benedikt Meurer:\n // https://benediktmeurer.de/2018/03/23/impact-of-polymorphism-on-component-based-frameworks-like-react/\n super(destination);\n this._next = onNext\n ? function (this: OperatorSubscriber, value: T) {\n try {\n onNext(value);\n } catch (err) {\n destination.error(err);\n }\n }\n : super._next;\n this._error = onError\n ? function (this: OperatorSubscriber, err: any) {\n try {\n onError(err);\n } catch (err) {\n // Send any errors that occur down stream.\n destination.error(err);\n } finally {\n // Ensure finalization.\n this.unsubscribe();\n }\n }\n : super._error;\n this._complete = onComplete\n ? function (this: OperatorSubscriber) {\n try {\n onComplete();\n } catch (err) {\n // Send any errors that occur down stream.\n destination.error(err);\n } finally {\n // Ensure finalization.\n this.unsubscribe();\n }\n }\n : super._complete;\n }\n\n unsubscribe() {\n if (!this.shouldUnsubscribe || this.shouldUnsubscribe()) {\n const { closed } = this;\n super.unsubscribe();\n // Execute additional teardown if we have any and we didn't already do so.\n !closed && this.onFinalize?.();\n }\n }\n}\n", "import { Subscription } from '../Subscription';\n\ninterface AnimationFrameProvider {\n schedule(callback: FrameRequestCallback): Subscription;\n requestAnimationFrame: typeof requestAnimationFrame;\n cancelAnimationFrame: typeof cancelAnimationFrame;\n delegate:\n | {\n requestAnimationFrame: typeof requestAnimationFrame;\n cancelAnimationFrame: typeof cancelAnimationFrame;\n }\n | undefined;\n}\n\nexport const animationFrameProvider: AnimationFrameProvider = {\n // When accessing the delegate, use the variable rather than `this` so that\n // the functions can be called without being bound to the provider.\n schedule(callback) {\n let request = requestAnimationFrame;\n let cancel: typeof cancelAnimationFrame | undefined = cancelAnimationFrame;\n const { delegate } = animationFrameProvider;\n if (delegate) {\n request = delegate.requestAnimationFrame;\n cancel = delegate.cancelAnimationFrame;\n }\n const handle = request((timestamp) => {\n // Clear the cancel function. The request has been fulfilled, so\n // attempting to cancel the request upon unsubscription would be\n // pointless.\n cancel = undefined;\n callback(timestamp);\n });\n return new Subscription(() => cancel?.(handle));\n },\n requestAnimationFrame(...args) {\n const { delegate } = animationFrameProvider;\n return (delegate?.requestAnimationFrame || requestAnimationFrame)(...args);\n },\n cancelAnimationFrame(...args) {\n const { delegate } = animationFrameProvider;\n return (delegate?.cancelAnimationFrame || cancelAnimationFrame)(...args);\n },\n delegate: undefined,\n};\n", "import { createErrorClass } from './createErrorClass';\n\nexport interface ObjectUnsubscribedError extends Error {}\n\nexport interface ObjectUnsubscribedErrorCtor {\n /**\n * @deprecated Internal implementation detail. Do not construct error instances.\n * Cannot be tagged as internal: https://github.com/ReactiveX/rxjs/issues/6269\n */\n new (): ObjectUnsubscribedError;\n}\n\n/**\n * An error thrown when an action is invalid because the object has been\n * unsubscribed.\n *\n * @see {@link Subject}\n * @see {@link BehaviorSubject}\n *\n * @class ObjectUnsubscribedError\n */\nexport const ObjectUnsubscribedError: ObjectUnsubscribedErrorCtor = createErrorClass(\n (_super) =>\n function ObjectUnsubscribedErrorImpl(this: any) {\n _super(this);\n this.name = 'ObjectUnsubscribedError';\n this.message = 'object unsubscribed';\n }\n);\n", "import { Operator } from './Operator';\nimport { Observable } from './Observable';\nimport { Subscriber } from './Subscriber';\nimport { Subscription, EMPTY_SUBSCRIPTION } from './Subscription';\nimport { Observer, SubscriptionLike, TeardownLogic } from './types';\nimport { ObjectUnsubscribedError } from './util/ObjectUnsubscribedError';\nimport { arrRemove } from './util/arrRemove';\nimport { errorContext } from './util/errorContext';\n\n/**\n * A Subject is a special type of Observable that allows values to be\n * multicasted to many Observers. Subjects are like EventEmitters.\n *\n * Every Subject is an Observable and an Observer. You can subscribe to a\n * Subject, and you can call next to feed values as well as error and complete.\n */\nexport class Subject extends Observable implements SubscriptionLike {\n closed = false;\n\n private currentObservers: Observer[] | null = null;\n\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n observers: Observer[] = [];\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n isStopped = false;\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n hasError = false;\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n thrownError: any = null;\n\n /**\n * Creates a \"subject\" by basically gluing an observer to an observable.\n *\n * @nocollapse\n * @deprecated Recommended you do not use. Will be removed at some point in the future. Plans for replacement still under discussion.\n */\n static create: (...args: any[]) => any = (destination: Observer, source: Observable): AnonymousSubject => {\n return new AnonymousSubject(destination, source);\n };\n\n constructor() {\n // NOTE: This must be here to obscure Observable's constructor.\n super();\n }\n\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n lift(operator: Operator): Observable {\n const subject = new AnonymousSubject(this, this);\n subject.operator = operator as any;\n return subject as any;\n }\n\n /** @internal */\n protected _throwIfClosed() {\n if (this.closed) {\n throw new ObjectUnsubscribedError();\n }\n }\n\n next(value: T) {\n errorContext(() => {\n this._throwIfClosed();\n if (!this.isStopped) {\n if (!this.currentObservers) {\n this.currentObservers = Array.from(this.observers);\n }\n for (const observer of this.currentObservers) {\n observer.next(value);\n }\n }\n });\n }\n\n error(err: any) {\n errorContext(() => {\n this._throwIfClosed();\n if (!this.isStopped) {\n this.hasError = this.isStopped = true;\n this.thrownError = err;\n const { observers } = this;\n while (observers.length) {\n observers.shift()!.error(err);\n }\n }\n });\n }\n\n complete() {\n errorContext(() => {\n this._throwIfClosed();\n if (!this.isStopped) {\n this.isStopped = true;\n const { observers } = this;\n while (observers.length) {\n observers.shift()!.complete();\n }\n }\n });\n }\n\n unsubscribe() {\n this.isStopped = this.closed = true;\n this.observers = this.currentObservers = null!;\n }\n\n get observed() {\n return this.observers?.length > 0;\n }\n\n /** @internal */\n protected _trySubscribe(subscriber: Subscriber): TeardownLogic {\n this._throwIfClosed();\n return super._trySubscribe(subscriber);\n }\n\n /** @internal */\n protected _subscribe(subscriber: Subscriber): Subscription {\n this._throwIfClosed();\n this._checkFinalizedStatuses(subscriber);\n return this._innerSubscribe(subscriber);\n }\n\n /** @internal */\n protected _innerSubscribe(subscriber: Subscriber) {\n const { hasError, isStopped, observers } = this;\n if (hasError || isStopped) {\n return EMPTY_SUBSCRIPTION;\n }\n this.currentObservers = null;\n observers.push(subscriber);\n return new Subscription(() => {\n this.currentObservers = null;\n arrRemove(observers, subscriber);\n });\n }\n\n /** @internal */\n protected _checkFinalizedStatuses(subscriber: Subscriber) {\n const { hasError, thrownError, isStopped } = this;\n if (hasError) {\n subscriber.error(thrownError);\n } else if (isStopped) {\n subscriber.complete();\n }\n }\n\n /**\n * Creates a new Observable with this Subject as the source. You can do this\n * to create custom Observer-side logic of the Subject and conceal it from\n * code that uses the Observable.\n * @return {Observable} Observable that the Subject casts to\n */\n asObservable(): Observable {\n const observable: any = new Observable();\n observable.source = this;\n return observable;\n }\n}\n\n/**\n * @class AnonymousSubject\n */\nexport class AnonymousSubject extends Subject {\n constructor(\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n public destination?: Observer,\n source?: Observable\n ) {\n super();\n this.source = source;\n }\n\n next(value: T) {\n this.destination?.next?.(value);\n }\n\n error(err: any) {\n this.destination?.error?.(err);\n }\n\n complete() {\n this.destination?.complete?.();\n }\n\n /** @internal */\n protected _subscribe(subscriber: Subscriber): Subscription {\n return this.source?.subscribe(subscriber) ?? EMPTY_SUBSCRIPTION;\n }\n}\n", "import { TimestampProvider } from '../types';\n\ninterface DateTimestampProvider extends TimestampProvider {\n delegate: TimestampProvider | undefined;\n}\n\nexport const dateTimestampProvider: DateTimestampProvider = {\n now() {\n // Use the variable rather than `this` so that the function can be called\n // without being bound to the provider.\n return (dateTimestampProvider.delegate || Date).now();\n },\n delegate: undefined,\n};\n", "import { Subject } from './Subject';\nimport { TimestampProvider } from './types';\nimport { Subscriber } from './Subscriber';\nimport { Subscription } from './Subscription';\nimport { dateTimestampProvider } from './scheduler/dateTimestampProvider';\n\n/**\n * A variant of {@link Subject} that \"replays\" old values to new subscribers by emitting them when they first subscribe.\n *\n * `ReplaySubject` has an internal buffer that will store a specified number of values that it has observed. Like `Subject`,\n * `ReplaySubject` \"observes\" values by having them passed to its `next` method. When it observes a value, it will store that\n * value for a time determined by the configuration of the `ReplaySubject`, as passed to its constructor.\n *\n * When a new subscriber subscribes to the `ReplaySubject` instance, it will synchronously emit all values in its buffer in\n * a First-In-First-Out (FIFO) manner. The `ReplaySubject` will also complete, if it has observed completion; and it will\n * error if it has observed an error.\n *\n * There are two main configuration items to be concerned with:\n *\n * 1. `bufferSize` - This will determine how many items are stored in the buffer, defaults to infinite.\n * 2. `windowTime` - The amount of time to hold a value in the buffer before removing it from the buffer.\n *\n * Both configurations may exist simultaneously. So if you would like to buffer a maximum of 3 values, as long as the values\n * are less than 2 seconds old, you could do so with a `new ReplaySubject(3, 2000)`.\n *\n * ### Differences with BehaviorSubject\n *\n * `BehaviorSubject` is similar to `new ReplaySubject(1)`, with a couple of exceptions:\n *\n * 1. `BehaviorSubject` comes \"primed\" with a single value upon construction.\n * 2. `ReplaySubject` will replay values, even after observing an error, where `BehaviorSubject` will not.\n *\n * @see {@link Subject}\n * @see {@link BehaviorSubject}\n * @see {@link shareReplay}\n */\nexport class ReplaySubject extends Subject {\n private _buffer: (T | number)[] = [];\n private _infiniteTimeWindow = true;\n\n /**\n * @param bufferSize The size of the buffer to replay on subscription\n * @param windowTime The amount of time the buffered items will stay buffered\n * @param timestampProvider An object with a `now()` method that provides the current timestamp. This is used to\n * calculate the amount of time something has been buffered.\n */\n constructor(\n private _bufferSize = Infinity,\n private _windowTime = Infinity,\n private _timestampProvider: TimestampProvider = dateTimestampProvider\n ) {\n super();\n this._infiniteTimeWindow = _windowTime === Infinity;\n this._bufferSize = Math.max(1, _bufferSize);\n this._windowTime = Math.max(1, _windowTime);\n }\n\n next(value: T): void {\n const { isStopped, _buffer, _infiniteTimeWindow, _timestampProvider, _windowTime } = this;\n if (!isStopped) {\n _buffer.push(value);\n !_infiniteTimeWindow && _buffer.push(_timestampProvider.now() + _windowTime);\n }\n this._trimBuffer();\n super.next(value);\n }\n\n /** @internal */\n protected _subscribe(subscriber: Subscriber): Subscription {\n this._throwIfClosed();\n this._trimBuffer();\n\n const subscription = this._innerSubscribe(subscriber);\n\n const { _infiniteTimeWindow, _buffer } = this;\n // We use a copy here, so reentrant code does not mutate our array while we're\n // emitting it to a new subscriber.\n const copy = _buffer.slice();\n for (let i = 0; i < copy.length && !subscriber.closed; i += _infiniteTimeWindow ? 1 : 2) {\n subscriber.next(copy[i] as T);\n }\n\n this._checkFinalizedStatuses(subscriber);\n\n return subscription;\n }\n\n private _trimBuffer() {\n const { _bufferSize, _timestampProvider, _buffer, _infiniteTimeWindow } = this;\n // If we don't have an infinite buffer size, and we're over the length,\n // use splice to truncate the old buffer values off. Note that we have to\n // double the size for instances where we're not using an infinite time window\n // because we're storing the values and the timestamps in the same array.\n const adjustedBufferSize = (_infiniteTimeWindow ? 1 : 2) * _bufferSize;\n _bufferSize < Infinity && adjustedBufferSize < _buffer.length && _buffer.splice(0, _buffer.length - adjustedBufferSize);\n\n // Now, if we're not in an infinite time window, remove all values where the time is\n // older than what is allowed.\n if (!_infiniteTimeWindow) {\n const now = _timestampProvider.now();\n let last = 0;\n // Search the array for the first timestamp that isn't expired and\n // truncate the buffer up to that point.\n for (let i = 1; i < _buffer.length && (_buffer[i] as number) <= now; i += 2) {\n last = i;\n }\n last && _buffer.splice(0, last + 1);\n }\n }\n}\n", "import { Scheduler } from '../Scheduler';\nimport { Subscription } from '../Subscription';\nimport { SchedulerAction } from '../types';\n\n/**\n * A unit of work to be executed in a `scheduler`. An action is typically\n * created from within a {@link SchedulerLike} and an RxJS user does not need to concern\n * themselves about creating and manipulating an Action.\n *\n * ```ts\n * class Action extends Subscription {\n * new (scheduler: Scheduler, work: (state?: T) => void);\n * schedule(state?: T, delay: number = 0): Subscription;\n * }\n * ```\n *\n * @class Action\n */\nexport class Action extends Subscription {\n constructor(scheduler: Scheduler, work: (this: SchedulerAction, state?: T) => void) {\n super();\n }\n /**\n * Schedules this action on its parent {@link SchedulerLike} for execution. May be passed\n * some context object, `state`. May happen at some point in the future,\n * according to the `delay` parameter, if specified.\n * @param {T} [state] Some contextual data that the `work` function uses when\n * called by the Scheduler.\n * @param {number} [delay] Time to wait before executing the work, where the\n * time unit is implicit and defined by the Scheduler.\n * @return {void}\n */\n public schedule(state?: T, delay: number = 0): Subscription {\n return this;\n }\n}\n", "import type { TimerHandle } from './timerHandle';\ntype SetIntervalFunction = (handler: () => void, timeout?: number, ...args: any[]) => TimerHandle;\ntype ClearIntervalFunction = (handle: TimerHandle) => void;\n\ninterface IntervalProvider {\n setInterval: SetIntervalFunction;\n clearInterval: ClearIntervalFunction;\n delegate:\n | {\n setInterval: SetIntervalFunction;\n clearInterval: ClearIntervalFunction;\n }\n | undefined;\n}\n\nexport const intervalProvider: IntervalProvider = {\n // When accessing the delegate, use the variable rather than `this` so that\n // the functions can be called without being bound to the provider.\n setInterval(handler: () => void, timeout?: number, ...args) {\n const { delegate } = intervalProvider;\n if (delegate?.setInterval) {\n return delegate.setInterval(handler, timeout, ...args);\n }\n return setInterval(handler, timeout, ...args);\n },\n clearInterval(handle) {\n const { delegate } = intervalProvider;\n return (delegate?.clearInterval || clearInterval)(handle as any);\n },\n delegate: undefined,\n};\n", "import { Action } from './Action';\nimport { SchedulerAction } from '../types';\nimport { Subscription } from '../Subscription';\nimport { AsyncScheduler } from './AsyncScheduler';\nimport { intervalProvider } from './intervalProvider';\nimport { arrRemove } from '../util/arrRemove';\nimport { TimerHandle } from './timerHandle';\n\nexport class AsyncAction extends Action {\n public id: TimerHandle | undefined;\n public state?: T;\n // @ts-ignore: Property has no initializer and is not definitely assigned\n public delay: number;\n protected pending: boolean = false;\n\n constructor(protected scheduler: AsyncScheduler, protected work: (this: SchedulerAction, state?: T) => void) {\n super(scheduler, work);\n }\n\n public schedule(state?: T, delay: number = 0): Subscription {\n if (this.closed) {\n return this;\n }\n\n // Always replace the current state with the new state.\n this.state = state;\n\n const id = this.id;\n const scheduler = this.scheduler;\n\n //\n // Important implementation note:\n //\n // Actions only execute once by default, unless rescheduled from within the\n // scheduled callback. This allows us to implement single and repeat\n // actions via the same code path, without adding API surface area, as well\n // as mimic traditional recursion but across asynchronous boundaries.\n //\n // However, JS runtimes and timers distinguish between intervals achieved by\n // serial `setTimeout` calls vs. a single `setInterval` call. An interval of\n // serial `setTimeout` calls can be individually delayed, which delays\n // scheduling the next `setTimeout`, and so on. `setInterval` attempts to\n // guarantee the interval callback will be invoked more precisely to the\n // interval period, regardless of load.\n //\n // Therefore, we use `setInterval` to schedule single and repeat actions.\n // If the action reschedules itself with the same delay, the interval is not\n // canceled. If the action doesn't reschedule, or reschedules with a\n // different delay, the interval will be canceled after scheduled callback\n // execution.\n //\n if (id != null) {\n this.id = this.recycleAsyncId(scheduler, id, delay);\n }\n\n // Set the pending flag indicating that this action has been scheduled, or\n // has recursively rescheduled itself.\n this.pending = true;\n\n this.delay = delay;\n // If this action has already an async Id, don't request a new one.\n this.id = this.id ?? this.requestAsyncId(scheduler, this.id, delay);\n\n return this;\n }\n\n protected requestAsyncId(scheduler: AsyncScheduler, _id?: TimerHandle, delay: number = 0): TimerHandle {\n return intervalProvider.setInterval(scheduler.flush.bind(scheduler, this), delay);\n }\n\n protected recycleAsyncId(_scheduler: AsyncScheduler, id?: TimerHandle, delay: number | null = 0): TimerHandle | undefined {\n // If this action is rescheduled with the same delay time, don't clear the interval id.\n if (delay != null && this.delay === delay && this.pending === false) {\n return id;\n }\n // Otherwise, if the action's delay time is different from the current delay,\n // or the action has been rescheduled before it's executed, clear the interval id\n if (id != null) {\n intervalProvider.clearInterval(id);\n }\n\n return undefined;\n }\n\n /**\n * Immediately executes this action and the `work` it contains.\n * @return {any}\n */\n public execute(state: T, delay: number): any {\n if (this.closed) {\n return new Error('executing a cancelled action');\n }\n\n this.pending = false;\n const error = this._execute(state, delay);\n if (error) {\n return error;\n } else if (this.pending === false && this.id != null) {\n // Dequeue if the action didn't reschedule itself. Don't call\n // unsubscribe(), because the action could reschedule later.\n // For example:\n // ```\n // scheduler.schedule(function doWork(counter) {\n // /* ... I'm a busy worker bee ... */\n // var originalAction = this;\n // /* wait 100ms before rescheduling the action */\n // setTimeout(function () {\n // originalAction.schedule(counter + 1);\n // }, 100);\n // }, 1000);\n // ```\n this.id = this.recycleAsyncId(this.scheduler, this.id, null);\n }\n }\n\n protected _execute(state: T, _delay: number): any {\n let errored: boolean = false;\n let errorValue: any;\n try {\n this.work(state);\n } catch (e) {\n errored = true;\n // HACK: Since code elsewhere is relying on the \"truthiness\" of the\n // return here, we can't have it return \"\" or 0 or false.\n // TODO: Clean this up when we refactor schedulers mid-version-8 or so.\n errorValue = e ? e : new Error('Scheduled action threw falsy error');\n }\n if (errored) {\n this.unsubscribe();\n return errorValue;\n }\n }\n\n unsubscribe() {\n if (!this.closed) {\n const { id, scheduler } = this;\n const { actions } = scheduler;\n\n this.work = this.state = this.scheduler = null!;\n this.pending = false;\n\n arrRemove(actions, this);\n if (id != null) {\n this.id = this.recycleAsyncId(scheduler, id, null);\n }\n\n this.delay = null!;\n super.unsubscribe();\n }\n }\n}\n", "import { Action } from './scheduler/Action';\nimport { Subscription } from './Subscription';\nimport { SchedulerLike, SchedulerAction } from './types';\nimport { dateTimestampProvider } from './scheduler/dateTimestampProvider';\n\n/**\n * An execution context and a data structure to order tasks and schedule their\n * execution. Provides a notion of (potentially virtual) time, through the\n * `now()` getter method.\n *\n * Each unit of work in a Scheduler is called an `Action`.\n *\n * ```ts\n * class Scheduler {\n * now(): number;\n * schedule(work, delay?, state?): Subscription;\n * }\n * ```\n *\n * @class Scheduler\n * @deprecated Scheduler is an internal implementation detail of RxJS, and\n * should not be used directly. Rather, create your own class and implement\n * {@link SchedulerLike}. Will be made internal in v8.\n */\nexport class Scheduler implements SchedulerLike {\n public static now: () => number = dateTimestampProvider.now;\n\n constructor(private schedulerActionCtor: typeof Action, now: () => number = Scheduler.now) {\n this.now = now;\n }\n\n /**\n * A getter method that returns a number representing the current time\n * (at the time this function was called) according to the scheduler's own\n * internal clock.\n * @return {number} A number that represents the current time. May or may not\n * have a relation to wall-clock time. May or may not refer to a time unit\n * (e.g. milliseconds).\n */\n public now: () => number;\n\n /**\n * Schedules a function, `work`, for execution. May happen at some point in\n * the future, according to the `delay` parameter, if specified. May be passed\n * some context object, `state`, which will be passed to the `work` function.\n *\n * The given arguments will be processed an stored as an Action object in a\n * queue of actions.\n *\n * @param {function(state: ?T): ?Subscription} work A function representing a\n * task, or some unit of work to be executed by the Scheduler.\n * @param {number} [delay] Time to wait before executing the work, where the\n * time unit is implicit and defined by the Scheduler itself.\n * @param {T} [state] Some contextual data that the `work` function uses when\n * called by the Scheduler.\n * @return {Subscription} A subscription in order to be able to unsubscribe\n * the scheduled work.\n */\n public schedule(work: (this: SchedulerAction, state?: T) => void, delay: number = 0, state?: T): Subscription {\n return new this.schedulerActionCtor(this, work).schedule(state, delay);\n }\n}\n", "import { Scheduler } from '../Scheduler';\nimport { Action } from './Action';\nimport { AsyncAction } from './AsyncAction';\nimport { TimerHandle } from './timerHandle';\n\nexport class AsyncScheduler extends Scheduler {\n public actions: Array> = [];\n /**\n * A flag to indicate whether the Scheduler is currently executing a batch of\n * queued actions.\n * @type {boolean}\n * @internal\n */\n public _active: boolean = false;\n /**\n * An internal ID used to track the latest asynchronous task such as those\n * coming from `setTimeout`, `setInterval`, `requestAnimationFrame`, and\n * others.\n * @type {any}\n * @internal\n */\n public _scheduled: TimerHandle | undefined;\n\n constructor(SchedulerAction: typeof Action, now: () => number = Scheduler.now) {\n super(SchedulerAction, now);\n }\n\n public flush(action: AsyncAction): void {\n const { actions } = this;\n\n if (this._active) {\n actions.push(action);\n return;\n }\n\n let error: any;\n this._active = true;\n\n do {\n if ((error = action.execute(action.state, action.delay))) {\n break;\n }\n } while ((action = actions.shift()!)); // exhaust the scheduler queue\n\n this._active = false;\n\n if (error) {\n while ((action = actions.shift()!)) {\n action.unsubscribe();\n }\n throw error;\n }\n }\n}\n", "import { AsyncAction } from './AsyncAction';\nimport { AsyncScheduler } from './AsyncScheduler';\n\n/**\n *\n * Async Scheduler\n *\n * Schedule task as if you used setTimeout(task, duration)\n *\n * `async` scheduler schedules tasks asynchronously, by putting them on the JavaScript\n * event loop queue. It is best used to delay tasks in time or to schedule tasks repeating\n * in intervals.\n *\n * If you just want to \"defer\" task, that is to perform it right after currently\n * executing synchronous code ends (commonly achieved by `setTimeout(deferredTask, 0)`),\n * better choice will be the {@link asapScheduler} scheduler.\n *\n * ## Examples\n * Use async scheduler to delay task\n * ```ts\n * import { asyncScheduler } from 'rxjs';\n *\n * const task = () => console.log('it works!');\n *\n * asyncScheduler.schedule(task, 2000);\n *\n * // After 2 seconds logs:\n * // \"it works!\"\n * ```\n *\n * Use async scheduler to repeat task in intervals\n * ```ts\n * import { asyncScheduler } from 'rxjs';\n *\n * function task(state) {\n * console.log(state);\n * this.schedule(state + 1, 1000); // `this` references currently executing Action,\n * // which we reschedule with new state and delay\n * }\n *\n * asyncScheduler.schedule(task, 3000, 0);\n *\n * // Logs:\n * // 0 after 3s\n * // 1 after 4s\n * // 2 after 5s\n * // 3 after 6s\n * ```\n */\n\nexport const asyncScheduler = new AsyncScheduler(AsyncAction);\n\n/**\n * @deprecated Renamed to {@link asyncScheduler}. Will be removed in v8.\n */\nexport const async = asyncScheduler;\n", "import { AsyncAction } from './AsyncAction';\nimport { AnimationFrameScheduler } from './AnimationFrameScheduler';\nimport { SchedulerAction } from '../types';\nimport { animationFrameProvider } from './animationFrameProvider';\nimport { TimerHandle } from './timerHandle';\n\nexport class AnimationFrameAction extends AsyncAction {\n constructor(protected scheduler: AnimationFrameScheduler, protected work: (this: SchedulerAction, state?: T) => void) {\n super(scheduler, work);\n }\n\n protected requestAsyncId(scheduler: AnimationFrameScheduler, id?: TimerHandle, delay: number = 0): TimerHandle {\n // If delay is greater than 0, request as an async action.\n if (delay !== null && delay > 0) {\n return super.requestAsyncId(scheduler, id, delay);\n }\n // Push the action to the end of the scheduler queue.\n scheduler.actions.push(this);\n // If an animation frame has already been requested, don't request another\n // one. If an animation frame hasn't been requested yet, request one. Return\n // the current animation frame request id.\n return scheduler._scheduled || (scheduler._scheduled = animationFrameProvider.requestAnimationFrame(() => scheduler.flush(undefined)));\n }\n\n protected recycleAsyncId(scheduler: AnimationFrameScheduler, id?: TimerHandle, delay: number = 0): TimerHandle | undefined {\n // If delay exists and is greater than 0, or if the delay is null (the\n // action wasn't rescheduled) but was originally scheduled as an async\n // action, then recycle as an async action.\n if (delay != null ? delay > 0 : this.delay > 0) {\n return super.recycleAsyncId(scheduler, id, delay);\n }\n // If the scheduler queue has no remaining actions with the same async id,\n // cancel the requested animation frame and set the scheduled flag to\n // undefined so the next AnimationFrameAction will request its own.\n const { actions } = scheduler;\n if (id != null && actions[actions.length - 1]?.id !== id) {\n animationFrameProvider.cancelAnimationFrame(id as number);\n scheduler._scheduled = undefined;\n }\n // Return undefined so the action knows to request a new async id if it's rescheduled.\n return undefined;\n }\n}\n", "import { AsyncAction } from './AsyncAction';\nimport { AsyncScheduler } from './AsyncScheduler';\n\nexport class AnimationFrameScheduler extends AsyncScheduler {\n public flush(action?: AsyncAction): void {\n this._active = true;\n // The async id that effects a call to flush is stored in _scheduled.\n // Before executing an action, it's necessary to check the action's async\n // id to determine whether it's supposed to be executed in the current\n // flush.\n // Previous implementations of this method used a count to determine this,\n // but that was unsound, as actions that are unsubscribed - i.e. cancelled -\n // are removed from the actions array and that can shift actions that are\n // scheduled to be executed in a subsequent flush into positions at which\n // they are executed within the current flush.\n const flushId = this._scheduled;\n this._scheduled = undefined;\n\n const { actions } = this;\n let error: any;\n action = action || actions.shift()!;\n\n do {\n if ((error = action.execute(action.state, action.delay))) {\n break;\n }\n } while ((action = actions[0]) && action.id === flushId && actions.shift());\n\n this._active = false;\n\n if (error) {\n while ((action = actions[0]) && action.id === flushId && actions.shift()) {\n action.unsubscribe();\n }\n throw error;\n }\n }\n}\n", "import { AnimationFrameAction } from './AnimationFrameAction';\nimport { AnimationFrameScheduler } from './AnimationFrameScheduler';\n\n/**\n *\n * Animation Frame Scheduler\n *\n * Perform task when `window.requestAnimationFrame` would fire\n *\n * When `animationFrame` scheduler is used with delay, it will fall back to {@link asyncScheduler} scheduler\n * behaviour.\n *\n * Without delay, `animationFrame` scheduler can be used to create smooth browser animations.\n * It makes sure scheduled task will happen just before next browser content repaint,\n * thus performing animations as efficiently as possible.\n *\n * ## Example\n * Schedule div height animation\n * ```ts\n * // html:
\n * import { animationFrameScheduler } from 'rxjs';\n *\n * const div = document.querySelector('div');\n *\n * animationFrameScheduler.schedule(function(height) {\n * div.style.height = height + \"px\";\n *\n * this.schedule(height + 1); // `this` references currently executing Action,\n * // which we reschedule with new state\n * }, 0, 0);\n *\n * // You will see a div element growing in height\n * ```\n */\n\nexport const animationFrameScheduler = new AnimationFrameScheduler(AnimationFrameAction);\n\n/**\n * @deprecated Renamed to {@link animationFrameScheduler}. Will be removed in v8.\n */\nexport const animationFrame = animationFrameScheduler;\n", "import { Observable } from '../Observable';\nimport { SchedulerLike } from '../types';\n\n/**\n * A simple Observable that emits no items to the Observer and immediately\n * emits a complete notification.\n *\n * Just emits 'complete', and nothing else.\n *\n * ![](empty.png)\n *\n * A simple Observable that only emits the complete notification. It can be used\n * for composing with other Observables, such as in a {@link mergeMap}.\n *\n * ## Examples\n *\n * Log complete notification\n *\n * ```ts\n * import { EMPTY } from 'rxjs';\n *\n * EMPTY.subscribe({\n * next: () => console.log('Next'),\n * complete: () => console.log('Complete!')\n * });\n *\n * // Outputs\n * // Complete!\n * ```\n *\n * Emit the number 7, then complete\n *\n * ```ts\n * import { EMPTY, startWith } from 'rxjs';\n *\n * const result = EMPTY.pipe(startWith(7));\n * result.subscribe(x => console.log(x));\n *\n * // Outputs\n * // 7\n * ```\n *\n * Map and flatten only odd numbers to the sequence `'a'`, `'b'`, `'c'`\n *\n * ```ts\n * import { interval, mergeMap, of, EMPTY } from 'rxjs';\n *\n * const interval$ = interval(1000);\n * const result = interval$.pipe(\n * mergeMap(x => x % 2 === 1 ? of('a', 'b', 'c') : EMPTY),\n * );\n * result.subscribe(x => console.log(x));\n *\n * // Results in the following to the console:\n * // x is equal to the count on the interval, e.g. (0, 1, 2, 3, ...)\n * // x will occur every 1000ms\n * // if x % 2 is equal to 1, print a, b, c (each on its own)\n * // if x % 2 is not equal to 1, nothing will be output\n * ```\n *\n * @see {@link Observable}\n * @see {@link NEVER}\n * @see {@link of}\n * @see {@link throwError}\n */\nexport const EMPTY = new Observable((subscriber) => subscriber.complete());\n\n/**\n * @param scheduler A {@link SchedulerLike} to use for scheduling\n * the emission of the complete notification.\n * @deprecated Replaced with the {@link EMPTY} constant or {@link scheduled} (e.g. `scheduled([], scheduler)`). Will be removed in v8.\n */\nexport function empty(scheduler?: SchedulerLike) {\n return scheduler ? emptyScheduled(scheduler) : EMPTY;\n}\n\nfunction emptyScheduled(scheduler: SchedulerLike) {\n return new Observable((subscriber) => scheduler.schedule(() => subscriber.complete()));\n}\n", "import { SchedulerLike } from '../types';\nimport { isFunction } from './isFunction';\n\nexport function isScheduler(value: any): value is SchedulerLike {\n return value && isFunction(value.schedule);\n}\n", "import { SchedulerLike } from '../types';\nimport { isFunction } from './isFunction';\nimport { isScheduler } from './isScheduler';\n\nfunction last(arr: T[]): T | undefined {\n return arr[arr.length - 1];\n}\n\nexport function popResultSelector(args: any[]): ((...args: unknown[]) => unknown) | undefined {\n return isFunction(last(args)) ? args.pop() : undefined;\n}\n\nexport function popScheduler(args: any[]): SchedulerLike | undefined {\n return isScheduler(last(args)) ? args.pop() : undefined;\n}\n\nexport function popNumber(args: any[], defaultValue: number): number {\n return typeof last(args) === 'number' ? args.pop()! : defaultValue;\n}\n", "export const isArrayLike = ((x: any): x is ArrayLike => x && typeof x.length === 'number' && typeof x !== 'function');", "import { isFunction } from \"./isFunction\";\n\n/**\n * Tests to see if the object is \"thennable\".\n * @param value the object to test\n */\nexport function isPromise(value: any): value is PromiseLike {\n return isFunction(value?.then);\n}\n", "import { InteropObservable } from '../types';\nimport { observable as Symbol_observable } from '../symbol/observable';\nimport { isFunction } from './isFunction';\n\n/** Identifies an input as being Observable (but not necessary an Rx Observable) */\nexport function isInteropObservable(input: any): input is InteropObservable {\n return isFunction(input[Symbol_observable]);\n}\n", "import { isFunction } from './isFunction';\n\nexport function isAsyncIterable(obj: any): obj is AsyncIterable {\n return Symbol.asyncIterator && isFunction(obj?.[Symbol.asyncIterator]);\n}\n", "/**\n * Creates the TypeError to throw if an invalid object is passed to `from` or `scheduled`.\n * @param input The object that was passed.\n */\nexport function createInvalidObservableTypeError(input: any) {\n // TODO: We should create error codes that can be looked up, so this can be less verbose.\n return new TypeError(\n `You provided ${\n input !== null && typeof input === 'object' ? 'an invalid object' : `'${input}'`\n } where a stream was expected. You can provide an Observable, Promise, ReadableStream, Array, AsyncIterable, or Iterable.`\n );\n}\n", "export function getSymbolIterator(): symbol {\n if (typeof Symbol !== 'function' || !Symbol.iterator) {\n return '@@iterator' as any;\n }\n\n return Symbol.iterator;\n}\n\nexport const iterator = getSymbolIterator();\n", "import { iterator as Symbol_iterator } from '../symbol/iterator';\nimport { isFunction } from './isFunction';\n\n/** Identifies an input as being an Iterable */\nexport function isIterable(input: any): input is Iterable {\n return isFunction(input?.[Symbol_iterator]);\n}\n", "import { ReadableStreamLike } from '../types';\nimport { isFunction } from './isFunction';\n\nexport async function* readableStreamLikeToAsyncGenerator(readableStream: ReadableStreamLike): AsyncGenerator {\n const reader = readableStream.getReader();\n try {\n while (true) {\n const { value, done } = await reader.read();\n if (done) {\n return;\n }\n yield value!;\n }\n } finally {\n reader.releaseLock();\n }\n}\n\nexport function isReadableStreamLike(obj: any): obj is ReadableStreamLike {\n // We don't want to use instanceof checks because they would return\n // false for instances from another Realm, like an - -
- - - - - - - - - - - - - - - -

Home Menu (Loaded a mnemonic)

- - - -
- - - - - - - -
-
-
-
- - - - - - - - - - \ No newline at end of file diff --git a/en/getting-started/settings/index.html b/en/getting-started/settings/index.html deleted file mode 100644 index 6c60d79f2..000000000 --- a/en/getting-started/settings/index.html +++ /dev/null @@ -1,1539 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - Settings - Krux - Open-source signing device firmware for Bitcoin - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - Skip to content - - -
-
- -
- - - - - - -
- - - - - - - -
- -
- - - - -
-
- - - -
-
-
- - - - - - - - - -
-
-
- - - - -
-
- - - - - - - -

Settings

- -

In the Krux home menu, there is a Settings entry. Below is a breakdown of the options you can change:

-

-

-

Bitcoin - Network

-

-

-

This option allows you to switch between mainnet (the default) and testnet. Testnet can be used to try out different wallet coordinators or for development.

-
- -

Encryption

-

-

-

Modify the encryption method and parameters to fit your needs. This will be used when storing encrypted mnemonics or creating encrypted QR codes. For more info see Krux Encrypted Mnemonics.

-
- -

PBKDF2 Iter. (Iterations)

-

-

-

When you enter the encryption key, it is not directly used to encrypt your data. In order to protect against brute force attacks, the key is derived multiple times using hashing functions. PBKDF2 (Password-Based Key Derivation Function) iterations stands for the amount of derivations that will be performed over your key prior to encrypt/decrypt your mnemonic.

-

If you increase this value it will make the encryption harder, at the cost of taking longer to encrypt/decrypt your mnemonics.

-

Values must be multiple of 10,000. This was done to save data space on QR codes.

-
- -

Encryption Mode

-

-

-

Choose between well known and widely used AES (Advanced Encryption Standard) modes:

-
AES-ECB
-

ECB (Electronic Codebook), its a simpler method where encryption data blocks are encrypted individually. It will be faster and simpler to encrypt, QR codes will have a lower density and will be easier to transcribe.

-
AES-CBC
-

CBC (Cipher-block Chaining) is considered more secure as in the first data block an initialization vector (IV) is used to add random data to the encryption. The encryption of subsequent blocks depends on the data from previous blocks, ensuring chaining.

-

Encryption will take longer because a snapshot will be needed to generate the IV. This IV will be stored together with encrypted data, making encrypted QR codes denser and harder to transcribe.

-
- -

Hardware

-

-

-

Customize the parameters available for your device and change printer settings.

-
- -

Encoder (Maix Dock only)

-

If your device has a rotary encoder, you can change the debounce threshold in milliseconds. With lower values, faster movements and navigation will be allowed.

-

The caveat is low values can cause issues, such as double step and unexpected movements, especially with lower quality encoders. If this is the case increase the value to make navigation more stable.

-

Display (Maix Amigo only)

-

-

Some Maix Amigo screens are different, here you can customize the BGR Colors, Flipped X Coordinates and Inverted Colors. For more info see FAQ

-
- -

Printer

-

-

-

You can set up a thermal printer or tell Krux to store a GRBL CNC instructions file on a SD card to machine QR codes

-

CNC

-

Define several machining parameters according to the desired size, material you'll use, and your CNC characteristics and capabilities.

-

Thermal

-

Printers can come with different baudrates from the manufacturer. By default, Krux assumes the connected printer will have a baudrate of 9600. If yours is different, you can change it here.

-

Also setup the TX Pin you'll use (e.g. 35 for M5stickV and 7 for Maix Amigo) and tweak other parameters according to your printer recommendations. For most printers you will only need to connect 2 cables, the device TX to the printer RX and ground. Consult the part list page for supported printers.

-

Driver

-

Here you choose between Thermal, CNC or none (default). Leave this setting to "none" if you won't use a printer and don't want to be bothered by print prompts.

-
- -

Touchscreen (Maix Amigo and Yahboom only)

-

-

If your device has touchscreen you can change the touch detection threshold. If it is being too sensitive or detecting false or ghost touches, you should increase the threshold value, making it less sensitive. The other way is also valid, reduce the threshold to make the screen more sensitive to touches.

-
- -

Language - Locale

-

-

-

Here you can change Krux to your desired language.

-
- -

Persist

-

-

-

Choose between flash (device's internal memory) or SD card for the place where your settings will be stored.

-
- -

Appearance

-

-

-

Configure screensaver time and change Krux to your desired theme.

-
- -

Screensaver time

-

-

-

Set how long to wait idle before the screensaver appears. Enter 0 to disable the screensaver.

-
- -

Theme

-

Choose your color theme according to your preference. Some themes may be more suitable for some devices, coordinator cameras and environments. As an example, it may be easier to scan QR codes from Krux devices using light theme in brighter environments.

-

- - - -

-

- - - -

-

Factory Settings

-

-

-

Restore device to factory settings and reboot.

-
- - - - - - - - - - - - - -
-
- - - -
- - - -
- - - -
-
-
-
- - - - - - - - - - \ No newline at end of file diff --git a/en/getting-started/usage/generating-a-mnemonic/index.html b/en/getting-started/usage/generating-a-mnemonic/index.html deleted file mode 100644 index 81851c70b..000000000 --- a/en/getting-started/usage/generating-a-mnemonic/index.html +++ /dev/null @@ -1,1395 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - Generating a Mnemonic - Krux - Open-source signing device firmware for Bitcoin - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - Skip to content - - -
-
- -
- - - - - - -
- - - - - - - -
- -
- - - - -
-
- - - -
-
-
- - - - - - - - - -
-
-
- - - - -
-
- - - - - - - -

Generating a Mnemonic

- -

Krux has support for creating 12 and 24-word mnemonic seed phrases. Since true entropy is difficult to produce, especially with an embedded device, we recommend to outsource entropy generation using dice rolls, but it is also possible to use camera as a source of entropy to quickly create a mnemonic. -At the start screen, once you select New Mnemonic, you will be taken to a second menu where you can choose to create a mnemonic via camera, via rolls of a D6 (standard six-sided die) or D20 (20-sided die).

-

-

-

Camera

-

(Experimental!) Choose between 12 or 24 words, then take a random picture and Krux will generate a mnemonic from the hash of the image bytes.

-

- - -

-
- -

Image Entropy Quality Estimation

-

-

-

During image capture, entropy quality estimation is displayed to assist you in obtaining a high-quality image source for your key. After a snapshot is taken, Shannon's entropy and pixel deviation indices are presented. Minimum thresholds are established to prevent the use of poor-quality images with low entropy for key generation. It's important to note that these values serve as indicators or estimations of entropy quality, but they are not absolute entropy values in a cryptographic context.

-
- -

Dice Rolls

-

Via D6

-

Choose between 12 or 24 words.

-

The entropy in a single roll of a D6 is 2.585 bits ( log2(6) ); therefore a minimum of a 50 rolls will be required for 128 bits of entropy, enough to generate a 12-word mnemonic. For 24 words, or an entropy of 256 bits, a minimum of 99 rolls will be required.

-

- - -

-
- -

Via D20

-

Since a D20 has more possible outcomes, the entropy is increased per roll to 4.322 bits ( log2(20) ). This means that only 30 rolls are necessary to create a 12-word mnemonic and 60 rolls for a 24-word mnemonic.

-

- - -

-
- -

Dice Rolls Entropy Quality Estimation

-

-

-

When you input your dice rolls, you'll see two progress bars filling up. The top progress bar shows how many rolls you've entered compared to the minimum number needed. The bottom progress bar shows the real-time calculated Shannon's entropy compared to the required minimum (128 bits for 12 words and 256 bits for 24 words). When the Shannon's entropy estimation reaches the recommended level, the progress bar will be full, and its frame will change color. If you've met the minimum number of rolls but the entropy estimation is still below the recommended level, a warning will appear, suggesting you add more rolls to increase entropy. -Note: Similar to image entropy quality estimation, dice rolls Shannon's entropy serves as an indicator and should not be considered an absolute measure of cryptographic entropy.

-

Stats for Nerds

-

A low Shannon's entropy value might suggest that your dice are biased or that there's a problem with how you're gathering entropy. To investigate further, examine the "Stats for Nerds" section to check the distribution of your rolls and look for any abnormalities.

-

- - -

-
- -

How it works

-

For dice rolls, Krux keeps track of every roll you enter and displays the cumulative string of outcomes after each roll.

-

When you have entered your final roll, Krux will hash this string using SHA256 and output the resulting hash to the screen so that you can verify it for yourself.

-

In the case a camera snapshot is used as source, image bytes, which contain pixels data in RGB565 format, will be hashed just like it is done with the dice rolls string.

-

- - -

-

Krux then takes this hash, runs unhexlify on it to encode it as bytes, and deterministically converts it into a mnemonic according to the BIP-39 Reference Implementation.

-

Note: For 12-word mnemonics, only the first half of the SHA256 hash is used (128 bits), while 24-word mnemonics use the full hash (256 bits).

-
- -

How to verify

-

Don't trust, verify. We encourage you not to trust any claim you cannot verify yourself. Therefore, there are wallets that use compatible algorithms to calculate the entropy derived from dice rolls. You can use the SeedSigner or Coldcard hardware wallets, or even the Bitcoiner Guide website, they share the same logic that Krux uses and will give the same mnemonic for the dice roll method.

-

Alternatives

-

You can use any other offline airgapped devices to generate your mnemonic. If you have an old Android smartphone that is offline (in airplane mode [no active CDMA or GSM chip], no Wifi connection, no Bluetooth and localization service turned off), you can use the Krux app for Android. If you want to use a regular PC, a common strategy is to boot the PC using Tails from a USB stick, without connecting the device to the internet, and then you can run Krux using our simulator, use a copy of the the Bitcoiner Guide website or even Ian Coleman's BIP-39 Tool. It's worth noting that both generate a QR code that Krux can read via the QR input method mentioned on the next page (Loading a Mnemonic).

- - - - - - - - - - - - - -
-
- - - -
- - - -
- - - -
-
-
-
- - - - - - - - - - \ No newline at end of file diff --git a/en/getting-started/usage/loading-a-mnemonic/index.html b/en/getting-started/usage/loading-a-mnemonic/index.html deleted file mode 100644 index c9cc97a15..000000000 --- a/en/getting-started/usage/loading-a-mnemonic/index.html +++ /dev/null @@ -1,1505 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - Loading a Mnemonic - Krux - Open-source signing device firmware for Bitcoin - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - Skip to content - - -
-
- -
- - - - - - -
- - - - - - - -
- -
- - - - -
-
- - - -
-
-
- - - - - - - - - -
-
-
- - - - -
-
- - - - - - - -

Loading a Mnemonic

- -

Once you have either a 12- or 24-word mnemonic, choose Load Mnemonic on Krux's start menu, and you will be presented with several input methods:

-

-

-

Input Methods

-

-

-

Via Camera

-

You can choose to use the camera to scan a QR code or Tiny Seed metal plate backup.

-
- -

QR Code

-

It's unpleasant having to manually enter 12 or 24 words every time you want to use Krux. To remedy this you can instead use the device's camera to read a QR code containing the words. Krux will decode QR codes of four types:

-
    -
  1. Plain text QR: The mnemonic words encoded as text, with words separated by spaces.
  2. -
  3. SeedQR: Basically, it is the mnemonic words of the respective BIP-39 numbers concatenated, encoded as text.
  4. -
  5. Compact SeedQR: Basically, it is the mnemonic words bits concatenated as bytes.
  6. -
  7. Encrypted Mnemonic: A specification created by Krux that encrypts the mnemonic words bits and adds some information about the encryption used.
  8. -
-

After opening your wallet via one of the manual methods you can use Krux to create QR codes of all types above, transcript them to paper or metal using the transcription helpers or attach a thermal printer to your Krux and print out the mnemonic. Check out the Printing section for more information. -You can also use an offline QR code generator for this (ideally on an airgapped device).

-

Tiny Seed

-

Tiny Seed is a compact metal plate mnemonic backup method. -Krux devices have machine vision capabilities that allow users to scan these metal plates and instantly load mnemonics engraved on them. To properly scan them place the Tiny Seed over a black background and paint the punched bits black to increase contrast. You can also scan the thermally printed version, or a filled template. Find templates to scan or print here.

-

Via Manual Input

-

-

-

Manually type Words, Word Numbers, Tiny Seed (toggle the bits or punches) or Stackbit (model 1248 metal plate backup).

-
- -

Words

-

-

-

Enter each word of your BIP-39 mnemonic one at a time. Krux will disable impossible-to-reach letters as you type and will attempt to autocomplete your words to speed up the process.

-

On your 12th or 24th word, you can leave it blank to have Krux generate the final checksum word of your mnemonic for you. This is useful when you choose a manual method to generate your mnemonic and want the final BIP-39 checksum word to be valid.

-
- -

Word Numbers

-

-

-
Decimal
-

Enter each word of your BIP-39 mnemonic as a number from 1 to 2048 one at a time. You can use this list for reference.

-
Hexadecimal and Octal
-

You can also enter your BIP-39 mnemonic word's numbers (1-2048) in hexadecimal format, with values ranging from 0x1 to 0x800, or in octal format, with values ranging from 01 to 04000. This is useful with some metal plate backups that uses those formats.

-
Final checksum word
-

On your 12th or 24th word, you can leave it blank to have Krux generate the final checksum word of your mnemonic for you. This is useful when you choose a manual method to generate your mnemonic and want the final BIP-39 checksum word to be valid.

-
- -

Tiny Seed (Bits)

-

-

-

Enter the BIP-39 mnemonic word's numbers (1-2048) in binary format, toggling necessary bits to recreate each of the word's respective number. The last word will have checksum bits dynamically toggled while you fill the bits.

-
- -

Stackbit 1248

-

-

-

Enter the BIP-39 mnemonic word's numbers (1-2048) using the Stackbit 1248 metal plate backup method, where each of the four digits of the word's number is a sum of the numbers marked (punched) 1, 2, 4, or 8. For example, to enter the word "pear", number 1297, you must punch (1)(2)(1+8=9)(1+2+4=7).

-
- -

From Storage

-

-

-

You can retrieve mnemonics previously stored on device's internal flash or external (SD card). All stored mnemonics are encrypted, to load them you'll have to enter the same key you used to encrypt them.

-
- -

Wallet loading sequence

-

Confirm Mnemonic Words

-

-

-

Once you have entered your mnemonic, you will be presented with the full list of words to confirm.

-
- -

Passphrase

-

-

-

After confirming the mnemonic words, you can optionally choose to type or scan a BIP-39 passphrase. When typing, swipe left or right to change modes if your device has a touchscreen. For scanning, you can also create a QR code from your offline passphrase in Tools.

-
- -

Fingerprint

-

-

-

The wallet's fingerprint, if you have it noted down, will help you make sure you entered the correct mnemonic and passphrase (optional) and will load the expected wallet.

-
- -

Single-sig or Multisig

-

-

-

After loading your mnemonic and passphrase (optional), you will be asked if you want to use it as part of a Single-sig or Multisig wallet.

-

Your choice here will subtly change the generated xpub that is used to set up your device in your wallet coordinator software. You can learn more about the difference in the following guides for using single-sig and multisig wallets.

-

Now, onto the main menu...

-
- - - - - - - - - - - - - -
-
- - - -
- - - -
- - - -
-
-
-
- - - - - - - - - - \ No newline at end of file diff --git a/en/getting-started/usage/navigating-the-main-menu/index.html b/en/getting-started/usage/navigating-the-main-menu/index.html deleted file mode 100644 index 65b7451dd..000000000 --- a/en/getting-started/usage/navigating-the-main-menu/index.html +++ /dev/null @@ -1,1614 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - Navigating the Main Menu - Krux - Open-source signing device firmware for Bitcoin - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - Skip to content - - -
-
- -
- - - - - - -
- - - - - - - -
- -
- - - - -
-
- - - -
-
-
- - - - - - - - - -
-
-
- - - - -
-
- - - - - - - -

Navigating the Main Menu

- -

After entering your mnemonic, and loading a wallet, you will find yourself on Krux's main menu. Below is a breakdown of the entries available:

-

-

-
- -

Mnemonic

-

-

-

This will open a new submenu with the following options to backup your mnemonic. If you set a printer driver, it will also give the option to print the backup!

-
- -

Words

-

-

-

Display the mnemonic words as text so you can write them down.

-
- -

Numbers

-

-

-

Display the mnemonic word numbers in decimal, hex, or octal format.

-
- -

Plaintext QR

-

-

-

Generate a QR containing the mnemonic words as regular text, where words are separated by spaces.

-
- -

Compact SeedQR

-

-

-

A QR code is created from a binary representation of mnemonic words. Format created by SeedSigner -backup-qr-compact

-
- -

SeedQR

-

-

-

Words are converted to their BIP-39 numeric indexes, those numbers are then concatenated as a string and finally converted to a QR code. Format created by SeedSigner

-
- -

Stackbit 1248

-

-

-

This metal backup format represents the BIP-39 mnemonic word's numbers (1-2048). Each of the four digits is converted to a sum of 1, 2, 4 or 8. This option does not print even if a printer driver is set.

-
- -

Tiny Seed

-

-

-

This metal backup format represents the BIP-39 mnemonic word's numbers (1-2048) in binary format on a metal plate, where the 1's are marked (punched) and the 0's are left intact.

-
- -

Encrypt Mnemonic

-

-

-

This feature allows you to back up your mnemonic by encrypting it and storing it on the device's flash memory, an SD card, or in QR code format. You can customize the encryption method and parameters in the settings.

-

For convenience, you may choose to store the encrypted mnemonic on flash memory or an SD card, but it is advisable not to rely solely on these methods for backup. Flash storage can degrade over time and may be subject to permanent damage, resulting in the loss of stored information.

-

When using any of the encryption methods, you will be prompted to enter an encryption key. This key can be provided in text or QR code format. Additionally, you have the option to set a custom ID for easier management of your mnemonics. If a custom key is not specified, the device's current loaded wallet fingerprint will be used as the ID.

-

Store on Flash

-

This option stores the encrypted mnemonic in the device's flash memory. You can decrypt and load it later through the "Load Mnemonic" -> "From Storage" option.

-

Store on SD Card

-

If an SD card is available, this option stores the encrypted mnemonic on it. You can decrypt and load it later through the "Load Mnemonic" -> "From Storage" option.

-

Encrypted QR Code

-

This option converts the encrypted mnemonic into a QR code. When you scan this QR code through "Load Mnemonic" -> "Via Camera" -> "QR Code," you will be prompted to enter the decryption key to load the mnemonic stored in it.

-

Learn more about Krux Mnemonics Encryption

-
- -

Extended Public Key

-

A menu will be presented with options to display your master extended public key (xPub) as text and as a QR code. Depending on whether a single-sig or multisig wallet was loaded, the options shown will be xPub, zPub, or ZPub. When displayed as text, the extended public key can be stored on an SD card if available. If you choose to export a QR code, you can not only scan it but also save it as an image on an SD card or print it if a thermal printer is attached.

-

- - - - -

-

All QR codes will contain key origin information in key expressions. If your coordinator cannot parse this information, it will not be capable of importing the wallet's fingerprint. As a result, Krux will not be able to sign transactions created by it unless you manually add the fingerprint so that it can be used to create Krux compatible PSBTs.

-
- -

Wallet Descriptor

-

When you select this option for the first time, you will be prompted to load a wallet. The camera will activate, and you will need to scan a wallet backup QR code generated by your wallet coordinator software. If the scan is successful, a preview of the wallet will be displayed for confirmation. If you abort the scan, you can alternatively load the wallet descriptor from an SD card.

-

- - -

-

If you access this option again after having loaded your wallet, you will see the wallet's name, fingerprints and the abbreviated xPubs of all cosigners, along with a QR code containing the exact data that was initially loaded. If an SD card is inserted, you can save the descriptor to it. Additionally, if you have a thermal printer attached, you can print this QR code.

-

Please note that once a wallet is loaded, it cannot be changed. To load a different wallet, you will need to restart the device and re-enter your mnemonic.

-
- -

Address

-

-

-

Scan, verify, export or print your wallet addresses.

-
- -

Scan Address

-

-

-

This option turns on the camera and allows you to scan in a QR code of a receive address. Upon scanning, it will render its own QR code of the address back to the display along with the (text) address below it. You could use this feature to scan the address of someone you want to send coins to and display the QR back to your wallet coordinator rather than copy-pasting an address.

-

If you have a thermal printer attached, you can also print this QR code.

-

After proceeding through this screen, you will be asked if you want to check that the address belongs to your wallet. If you confirm, it will exhaustively search through as many addresses derived from your wallet as you want in order to find a match.

-

This option exists as an extra security check to verify that the address your wallet coordinator has generated is authentic and belongs to your wallet.

-
- -

Receive Addresses

-

-

-

List your wallet receiving addresses, you can browse to select an arbitrary address to show your QRCode and print if you want

-
- -

Change Addresses

-

-

-

List your wallet change addresses, you can browse to select an arbitrary address to show your QRCode and print if you want

-
- -

Sign

-

-

-

Under Sign, you can choose to sign a PSBT or a message. You can load both PSBTs and messages scanning QR codes or loading from files on a SD card.

-
- -

PSBT

-

-

-

To sign a Bitcoin PSBT, you have the following options:

-
    -
  1. Scan an Animated QR Code: Turn on the camera and scan an animated QR code of a PSBT generated by your wallet coordinator software.
  2. -
  3. Load from SD Card: Load an unsigned PSBT file from your SD card.
  4. -
-

Upon loading the PSBT, you will be presented with a preview showing the amount of BTC being sent, the recipient's address, and the transaction fee. Amounts are displayed according to your locale and the International Bureau of Weights and Measures, while still adhering to the concept of the Satcomma standard format.

-

If you choose to proceed and sign the transaction, the signed PSBT can be exported in two ways:

-
    -
  1. As an animated QR code, which can be scanned back into your coordinator wallet.
  2. -
  3. As a signed PSBT file, which can be saved to your SD card and then loaded back into your coordinator wallet for broadcasting.
  4. -
-

If a thermal printer is attached to your device, you can also print the PSBT QR codes for record-keeping or further processing.

-
- -

Message

-

Similar to PSBTs, Krux can load, sign, and export signatures for messages. This feature allows you to attest not only to the ownership of the messages themselves but also to the ownership of Bitcoin addresses and the authorship of documents and files.

-
Standard Messages and Files
-

-

-

You can scan or load a file from an SD card, the content can be plaintext or the SHA-256 hash of a message. Upon loading, you will be shown a preview of the message's SHA-256 hash for confirmation before signing.

-

If you confirm, a signature will be generated, and you will see a base64-encoded version of it. You can then choose to export it as a QR code or save it to an SD card. If a thermal printer is attached, you can also print the QR code.

-

Following this, you will see and be allowed to export your raw (master) public key in hexadecimal form, which can be used by others to verify your signature. If a thermal printer is attached, you can also print this QR code.

-

This feature is used to sign Krux releases, airgapped, using a Krux device.

-
- -
Messages at Address
-

-

-

Coordinators like Sparrow and Specter offer the possibility to sign messages at a Bitcoin receive address, allowing you to attest ownership of that address. Krux will detect if the message is of this type and present a similar workflow for signing. The main difference is that the address will be displayed along with the raw message, and since the message is signed with a derived address instead of the master public key, Krux won't offer the option to export the raw public key after the signature.

- - - - - - - - - - - - - -
-
- - - -
- - - -
- - - -
-
-
-
- - - - - - - - - - \ No newline at end of file diff --git a/en/getting-started/usage/using-a-multisig-wallet/index.html b/en/getting-started/usage/using-a-multisig-wallet/index.html deleted file mode 100644 index 4d1b23675..000000000 --- a/en/getting-started/usage/using-a-multisig-wallet/index.html +++ /dev/null @@ -1,1534 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - Using a Multisig Wallet - Krux - Open-source signing device firmware for Bitcoin - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - Skip to content - - -
-
- -
- - - - - - -
- - - - - - - -
- -
- - - - -
-
- - - -
-
-
- - - - - - - - - -
-
-
- - - - -
-
- - - - - - - -

Using a Multisig Wallet

- -

This guide assumes you have already created a mnemonic for each cosigner that will be in your multisig wallet. If that is not the case, head over to the Generating a Mnemonic page and complete those steps first.

-

When entering your mnemonics into Krux, make sure to select Multisig for all keys in your multisignature scheme. The choice of Single-sig vs. Multisig at this point will change the derivation path used to generate your master extended public key (xpub) which will affect how wallet software handles it.

-

-

-

Selecting Multisig will derive an xpub using the derivation path m/48'/0'/0'/2' on mainnet and m/48'/1'/0'/2' on testnet, which indicates to wallet software that a Multisig Wallet script type should be used, specifically the Native Segwit script P2WSH, or just wsh.

-

Note: While not required, the load of the Wallet Descriptor is strongly recommended to load your multisig wallet into Krux before signing a PSBT as it will allow the device to verify that the transaction it is signing has the correct list of cosigners as defined in the wallet. It is also useful if you wish to print a backup of the wallet or visually inspect the list of cosigners being output by your wallet coordinator.

-

Specter Desktop

-

Create the wallet

-

In Specter Desktop, you will need to import your public key by adding a new device. Press the Add new device button on the left side of the app.

-

-

Krux is not listed as one of the available device types on the Add Device screen, so you will need to select the Other option.

-

-

You will be taken to the Upload Keys screen where you can choose to Scan QR code.

-

-

On your Krux, navigate to the Extended Public Key option under the main menu and show the first QR code to Specter Desktop.

-

-

-

It should import the xpub and display the Purpose as #0 Multisig Sig (Segwit).

-

-

Give the device a name and press Continue. You should see the new device in the devices list on the left side of the app.

-

-

Repeat this process for every key that will be in your multisig wallet.

-

Once all key devices have been added, you can make a wallet using them. Press the Add new wallet button on the left side of the app.

-

-

Choose to create a Multisignature wallet when it asks which type of wallet you want on the following screen.

-

-

Select the devices you just added and press Continue.

-

-

Give your wallet a name, make sure to select Segwit for the wallet type, and decide how many of your keys are required to sign a transaction, then press Create wallet.

-

-

Congrats, you just created a multisig wallet!

-

Load the wallet into Krux

-

Load the wallet into Krux by going to the Settings page in Specter Desktop, then click the Export tab. There, press the Export button to display a QR code of your wallet.

-

-

In Krux, select the Wallet Descriptor menu item option and scan the QR code.

-

-

-

If it worked, Krux should display the wallet information that it loaded:

-

-

-

Receive coins

-

The Receive screen should show your first receive address that you can send funds to.

-

-

Send coins

-

Go to Send in Specter Desktop, fill in the recipient address, amount, and any extra information you wish to supply, and click Create unsigned transaction.

-

-

You will now see a screen listing the devices in your wallet. Select the device you want to sign the PSBT transaction with.

-

Specter Desktop will display an animated QR code of the PSBT that you can scan with Krux by going to Sign > PSBT in its main menu. After scanning, Krux should display info about the transaction for you to confirm before signing.

-

-

-

Once you have confirmed, Krux will begin animating a QR code of the signed transaction that you can scan into Specter Desktop.

-

-

-

In Specter Desktop, click Scan signed transaction and show it the QR. Each part of the QR code that is read will receive a ghost icon to indicate progress.

-

-

Once all parts of the QR code have been read, you should see a checkmark next to your device indicating its signature was added.

-

Repeat this process for the remaining keys in your wallet until you have a quorum of signatures.

-

After the final signature, a window will popup asking you to broadcast the transaction. Click Send transaction and your transaction should be broadcasted to the network!

-

-

Congratulations, you have learned how to send a transaction using Specter Desktop 🎉

-

Sparrow

-

Create the wallet

-

In Sparrow, create a new wallet by going to File > New Wallet and give it a name.

-

-

On the wallet screen, make sure to select a Multi Signature policy type with the Native Segwit (P2WSH) script type. Here you can decide how many keys will be in your multisig and how many should be required to sign a transaction.

-

-

Now, you will need to import your public key. To do so, press the Airgapped Hardware Wallet button under Keystores section. On the screen that pops up, look for Krux option and click its 📷 Scan... button.

-

-

-

On your Krux device, navigate to the Extended Public Key option in the main menu and click on XPUB - QR Code and show it to Sparrow.

-

-

-

It should import the xpub and show a key tab under Keystores section:

-

-

Repeat this process for every key that will be in your multisig wallet.

-

Once all keys have been added, click the blue Apply button to create your wallet.

-

Congrats, you just created a multisig wallet!

-

Load the wallet output descriptor into Krux

-

Load the wallet output descriptor into Krux by going to the Settings screen of the wallet in Sparrow, then click the (QR code symbol) button at Script Policy section to the right of the Descriptor: to display a QR code that you can scan with Krux.

-

-

In Krux, select the Wallet menu item option and scan the QR code.

-

-

-

If it worked, Krux should display the wallet information that it loaded:

-

-

-

Receive coins

-

Navigate to the Receive screen where you should see your first receive address that you can send funds to.

-

-

Send coins

-

Go to the Send screen, fill in the recipient address, amount, and any extra information you wish to supply, and click the blue Create Transaction button.

-

-

On the next screen, make sure that the Signing Wallet is the one you created and that the Sighash is set to All. Click the blue Finalize Transaction for Signing button.

-

-

On the next screen, click Show QR to make Sparrow display an animated QR code of the PSBT that you can scan with Krux by going to Sign > PSBT > Load from camera in its main menu.

-

-

After scanning, Krux should display info about the transaction for you to confirm before signing.

-

-

-

Once you have confirmed, Krux will begin animating a QR code of the signed transaction that you can scan into Sparrow.

-

-

-

In Sparrow, click Scan QR and show it the QR. A progress bar will indicate how many parts of the QR have been read.

-

Once all parts of the QR code have been read, you should see the signature bar partially fill indicating the signature was added.

-

Repeat this process for the remaining keys in your wallet until you have a quorum of signatures.

-

After the final signature, the bar will fill and two new buttons appear. -Click the blue Broadcast Transaction button and your transaction should be broadcasted to the network!

-

-

Congratulations, you have learned how to send a transaction using Sparrow 🎉

-

BlueWallet

-

Create the wallet

-

In BlueWallet, create a new wallet by either pressing the + button or scrolling to the right until you see the Add now button.

-

-

On the screen that pops up, add a name for your wallet, tap on Vault, and tap Create to begin creating a multisig wallet.

-

-

On the following screen, you can proceed with the defaults or adjust the number of cosigners (default is 2-of-3) as needed by going into the Vault Settings. Make sure to leave the script type as p2wsh.

-

-

-

Continue and you will be taken to a screen where you can import your keys.

-

Tap Import under the first Vault Key.

-

-

Tap again on Scan or import a file to begin scanning a QR code.

-

On your Krux, navigate to the Extended Public Key option under the main menu and show the first QR code to BlueWallet.

-

-

-

You should see a green checkmark next to the key if successful.

-

-

Repeat this process for every key that will be in your multisig wallet.

-

Once all keys have been added, tap Create.

-

-

From here, you can send or receive.

-

-

Congrats, you just created a multisig wallet!

-

Load the wallet into Krux

-

Load the wallet into Krux by tapping the ellipsis in the top-right to see the wallet settings.

-

-

From here, tap Export Coordination Setup in order to display a QR code of your wallet.

-

-

In Krux, select the Wallet Descriptor menu item option and scan the QR code.

-

-

-

If it worked, Krux should display the wallet information that it loaded:

-

-

-

Receive coins

-

Navigate to the Receive screen where you should see your first receive address that you can send funds to.

-

-

Send coins

-

Go to the Send screen, fill in the recipient address, amount, and any extra information you wish to supply, and tap Next.

-

-

You should see an animated QR code of the PSBT that you can scan with Krux by going to Sign > PSBT in its main menu.

-

After scanning, Krux should display info about the transaction for you to confirm before signing.

-

-

-

Once you have confirmed, Krux will begin animating a QR code of the signed transaction that you can scan into BlueWallet.

-

-

-

Once all parts of the QR code have been read, you can then choose to broadcast the transaction, sending it to the network!

-

Congratulations, you have learned how to send a transaction using BlueWallet 🎉

- - - - - - - - - - - - - -
-
- - - -
- - - -
- - - -
-
-
-
- - - - - - - - - - \ No newline at end of file diff --git a/en/getting-started/usage/using-a-single-sig-wallet/index.html b/en/getting-started/usage/using-a-single-sig-wallet/index.html deleted file mode 100644 index 412582d61..000000000 --- a/en/getting-started/usage/using-a-single-sig-wallet/index.html +++ /dev/null @@ -1,1520 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - Using a Single-sig Wallet - Krux - Open-source signing device firmware for Bitcoin - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - Skip to content - - -
-
- -
- - - - - - -
- - - - - - - -
- -
- - - - -
-
- - - -
-
-
- - - - - - - - - -
-
-
- - - - -
-
- - - - - - - -

Using a Single-sig Wallet

- -

This guide assumes you have already created a mnemonic. If that is not the case, head over to the Generating a Mnemonic page and complete those steps first.

-

When entering your mnemonic into Krux, make sure to select Single-sig before proceeding. The choice of Single-sig vs. Multisig at this point will change the derivation path used to generate your master extended public key (xpub) which will affect how wallet software handles it.

-

-

-

Selecting Single-sig will derive an xpub using the derivation path m/84'/0'/0' on mainnet and m/84'/1'/0' on testnet, which indicates to wallet software that a Segregated Witness (Segwit) script type should be used. For single-sig wallets, this script is P2WPKH, or just wpkh.

-

Note: The load of the Wallet Descriptor step is unnecessary for signing PSBTs with Single-sig wallets since the script type (wpkh) and key are already known. However, this can be useful if you wish to print a backup of the wallet or want an additional sanity check.

-

Specter Desktop

-

Create the wallet

-

In Specter Desktop, you will need to import your public key by adding a new device. Press the Add new device button on the left side of the app.

-

-

Krux is not listed as one of the available device types on the Add Device screen, so you will need to select the Other option.

-

-

You will be taken to the Upload Keys screen where you can choose to Scan QR code.

-

-

On your Krux, navigate to the Extended Public Key option under the main menu and show the first QR code to Specter Desktop.

-

-

-

It should import the xpub and display the Purpose as #0 Single Sig (Segwit).

-

-

Give the device a name and press Continue. You should see the new device in the devices list on the left side of the app.

-

-

After you've added a device with your key to Specter Desktop, you can make a wallet using it. Press the Add new wallet button on the left side of the app.

-

-

Choose to create a Single key wallet when it asks which type of wallet you want on the following screen.

-

-

Select the device you just added.

-

-

Give your wallet a name and make sure to select Segwit for the wallet type, then press Create wallet.

-

-

Congrats, you just created a single-sig wallet with your key!

-

Load the wallet into Krux

-

Load the wallet into Krux by going to the Settings page in Specter Desktop, then click the Export tab. There, press the Export button to display a QR code of your wallet.

-

-

In Krux, select the Wallet Descriptor menu item option and scan the QR code.

-

-

-

If it worked, Krux should display the wallet information that it loaded:

-

-

-

Receive coins

-

Navigate to the Receive screen where you should see a receive address that you can send funds to.

-

-

Send coins

-

Go to Send in Specter Desktop, fill in the recipient address, amount, and any extra information you wish to supply, and click Create unsigned transaction.

-

-

You will now see a screen listing the devices in your wallet. Select the device you want to sign the PSBT transaction with.

-

Specter Desktop will display an animated QR code of the PSBT that you can scan with Krux by going to Sign > PSBT in its main menu. After scanning, Krux should display info about the transaction for you to confirm before signing.

-

-

-

Once you have confirmed, Krux will begin animating a QR code of the signed transaction that you can scan into Specter Desktop.

-

-

-

In Specter Desktop, click Scan signed transaction and show it the QR. Each part of the QR code that is read will receive a ghost icon to indicate progress.

-

-

Once all parts of the QR code have been read, you should see a window popup asking you to broadcast the transaction. Click Send transaction and your transaction should be broadcasted to the network!

-

-

Congratulations, you have learned how to send a transaction using Specter Desktop 🎉

-

Sparrow

-

Create the wallet

-

In Sparrow, create a new wallet by going to File > New Wallet and give it a name.

-

-

On the wallet screen, make sure to select a Single Signature policy type with the Native Segwit (P2WPKH) script type.

-

-

Now, you will need to import your public key. To do so, press the Airgapped Hardware Wallet button under Keystores section. On the screen that pops up, look for Krux option and click its 📷 Scan... button.

-

-

-

On your Krux device, navigate to the Extended Public Key option in the main menu and click on XPUB - QR Code and show it to Sparrow.

-

-

-

It should import the xpub and show a key tab under Keystores section:

-

-

If everything looks right, click the blue Apply button to create your wallet.

-

Congrats, you just created a single-sig wallet with your key!

-

Load the wallet output descriptor into Krux (optional)

-

Single-sig wallets don't need to perform this step, it is optional! -Load the wallet output descriptor into Krux by going to the Settings screen of the wallet in Sparrow, then click the (QR code symbol) button at Script Policy section to the right of the Descriptor: to display a QR code that you can scan with Krux.

-

-

In Krux, select the Wallet menu item option and scan the QR code.

-

-

-

If it worked, Krux should display the wallet information that it loaded:

-

-

-

Receive coins

-

Navigate to the Receive screen where you should see a receive address that you can send funds to.

-

-

Send coins

-

Go to the Send screen, fill in the recipient address, amount, and any extra information you wish to supply, and click the blue Create Transaction button.

-

-

On the next screen, make sure that the Signing Wallet is the one you created and that the Sighash is set to All. Click the blue Finalize Transaction for Signing button.

-

-

On the next screen, click Show QR to make Sparrow display an animated QR code of the PSBT that you can scan with Krux by going to Sign > PSBT > Load from camera in its main menu.

-

-

After scanning, Krux should display info about the transaction for you to confirm before signing.

-

-

-

Once you have confirmed, Krux will begin animating a QR code of the signed transaction that you can scan into Sparrow.

-

-

-

In Sparrow, click Scan QR and show it the QR. A progress bar will indicate how many parts of the QR have been read.

-

Once all parts of the QR code have been read, you should see the signature bar fill and two new buttons appear. -Click the blue Broadcast Transaction button and your transaction should be broadcasted to the network!

-

-

Congratulations, you have learned how to send a transaction using Sparrow 🎉

-

BlueWallet

-

Create the wallet

-

In BlueWallet, create a new wallet by either pressing the + button or scrolling to the right until you see the Add now button.

-

-

On the screen that pops up, tap Import wallet to import your public key.

-

-

On the following screen, tap Scan or import a file and it will begin trying to scan a QR code.

-

-

On your Krux, navigate to the Extended Public Key option under the main menu and make sure to show the second, zpub QR code to BlueWallet.

-

-

-

It should import the key and create a watch-only wallet. From here, you can send or receive.

-

-

Congrats, you just created a single-sig wallet with your key!

-

Load the wallet into Krux

-

Load the wallet into Krux by tapping the ellipsis in the top-right to see the wallet settings.

-

-

From here, tap Export/Backup in order to display a QR code of your wallet.

-

-

In Krux, select the Wallet Descriptor menu item option and scan the QR code.

-

-

-

If it worked, Krux should display the wallet information that it loaded:

-

-

-

Receive coins

-

Navigate to the Receive screen where you should see a receive address that you can send funds to.

-

-

Note that you can verify the receive address belongs to your wallet by using the Scan Address option.

-

Send coins

-

Go to the Send screen, fill in the recipient address, amount, and any extra information you wish to supply, and tap Next.

-

-

You should see an animated QR code of the PSBT that you can scan with Krux by going to Sign > PSBT in its main menu.

-

After scanning, Krux should display info about the transaction for you to confirm before signing.

-

-

-

Once you have confirmed, Krux will begin animating a QR code of the signed transaction that you can scan into BlueWallet.

-

-

-

Once all parts of the QR code have been read, you can then choose to broadcast the transaction, sending it to the network!

-

Congratulations, you have learned how to send a transaction using BlueWallet 🎉

- - - - - - - - - - - - - -
-
- - - -
- - - -
- - - -
-
-
-
- - - - - - - - - - \ No newline at end of file diff --git a/en/img/amigo-inside-switch-up.jpg b/en/img/amigo-inside-switch-up.jpg deleted file mode 100644 index 1727210f5..000000000 Binary files a/en/img/amigo-inside-switch-up.jpg and /dev/null differ diff --git a/en/img/blue/add-wallet-125.png b/en/img/blue/add-wallet-125.png deleted file mode 100644 index babe92c87..000000000 Binary files a/en/img/blue/add-wallet-125.png and /dev/null differ diff --git a/en/img/blue/add-wallet-150.png b/en/img/blue/add-wallet-150.png deleted file mode 100644 index 22fda2f76..000000000 Binary files a/en/img/blue/add-wallet-150.png and /dev/null differ diff --git a/en/img/blue/add-wallet-200.png b/en/img/blue/add-wallet-200.png deleted file mode 100644 index 38d032de8..000000000 Binary files a/en/img/blue/add-wallet-200.png and /dev/null differ diff --git a/en/img/blue/add-wallet-400.png b/en/img/blue/add-wallet-400.png deleted file mode 100644 index 0c9b7d7cc..000000000 Binary files a/en/img/blue/add-wallet-400.png and /dev/null differ diff --git a/en/img/blue/add-wallet-600.png b/en/img/blue/add-wallet-600.png deleted file mode 100644 index 89f1d9dc7..000000000 Binary files a/en/img/blue/add-wallet-600.png and /dev/null differ diff --git a/en/img/blue/add-wallet-button-125.png b/en/img/blue/add-wallet-button-125.png deleted file mode 100644 index d36ab1250..000000000 Binary files a/en/img/blue/add-wallet-button-125.png and /dev/null differ diff --git a/en/img/blue/add-wallet-button-150.png b/en/img/blue/add-wallet-button-150.png deleted file mode 100644 index cf613829c..000000000 Binary files a/en/img/blue/add-wallet-button-150.png and /dev/null differ diff --git a/en/img/blue/add-wallet-button-200.png b/en/img/blue/add-wallet-button-200.png deleted file mode 100644 index 67437f93a..000000000 Binary files a/en/img/blue/add-wallet-button-200.png and /dev/null differ diff --git a/en/img/blue/add-wallet-button-400.png b/en/img/blue/add-wallet-button-400.png deleted file mode 100644 index 120bd3ab6..000000000 Binary files a/en/img/blue/add-wallet-button-400.png and /dev/null differ diff --git a/en/img/blue/add-wallet-button-600.png b/en/img/blue/add-wallet-button-600.png deleted file mode 100644 index 88bfd39e2..000000000 Binary files a/en/img/blue/add-wallet-button-600.png and /dev/null differ diff --git a/en/img/blue/add-wallet-button.png b/en/img/blue/add-wallet-button.png deleted file mode 100644 index f16fe436e..000000000 Binary files a/en/img/blue/add-wallet-button.png and /dev/null differ diff --git a/en/img/blue/add-wallet-vault-125.png b/en/img/blue/add-wallet-vault-125.png deleted file mode 100644 index 9cfdbbcb8..000000000 Binary files a/en/img/blue/add-wallet-vault-125.png and /dev/null differ diff --git a/en/img/blue/add-wallet-vault-150.png b/en/img/blue/add-wallet-vault-150.png deleted file mode 100644 index 489075bb1..000000000 Binary files a/en/img/blue/add-wallet-vault-150.png and /dev/null differ diff --git a/en/img/blue/add-wallet-vault-200.png b/en/img/blue/add-wallet-vault-200.png deleted file mode 100644 index 34b19437d..000000000 Binary files a/en/img/blue/add-wallet-vault-200.png and /dev/null differ diff --git a/en/img/blue/add-wallet-vault-400.png b/en/img/blue/add-wallet-vault-400.png deleted file mode 100644 index 281aac64d..000000000 Binary files a/en/img/blue/add-wallet-vault-400.png and /dev/null differ diff --git a/en/img/blue/add-wallet-vault-600.png b/en/img/blue/add-wallet-vault-600.png deleted file mode 100644 index 54ec76840..000000000 Binary files a/en/img/blue/add-wallet-vault-600.png and /dev/null differ diff --git a/en/img/blue/add-wallet-vault.png b/en/img/blue/add-wallet-vault.png deleted file mode 100644 index cdb142122..000000000 Binary files a/en/img/blue/add-wallet-vault.png and /dev/null differ diff --git a/en/img/blue/add-wallet.png b/en/img/blue/add-wallet.png deleted file mode 100644 index 93cfa1f89..000000000 Binary files a/en/img/blue/add-wallet.png and /dev/null differ diff --git a/en/img/blue/export-multisig-wallet-125.png b/en/img/blue/export-multisig-wallet-125.png deleted file mode 100644 index fcee51472..000000000 Binary files a/en/img/blue/export-multisig-wallet-125.png and /dev/null differ diff --git a/en/img/blue/export-multisig-wallet-150.png b/en/img/blue/export-multisig-wallet-150.png deleted file mode 100644 index c79ef00de..000000000 Binary files a/en/img/blue/export-multisig-wallet-150.png and /dev/null differ diff --git a/en/img/blue/export-multisig-wallet-200.png b/en/img/blue/export-multisig-wallet-200.png deleted file mode 100644 index 3cc65e75f..000000000 Binary files a/en/img/blue/export-multisig-wallet-200.png and /dev/null differ diff --git a/en/img/blue/export-multisig-wallet-400.png b/en/img/blue/export-multisig-wallet-400.png deleted file mode 100644 index a3a54cdd1..000000000 Binary files a/en/img/blue/export-multisig-wallet-400.png and /dev/null differ diff --git a/en/img/blue/export-multisig-wallet-600.png b/en/img/blue/export-multisig-wallet-600.png deleted file mode 100644 index 820562747..000000000 Binary files a/en/img/blue/export-multisig-wallet-600.png and /dev/null differ diff --git a/en/img/blue/export-multisig-wallet.png b/en/img/blue/export-multisig-wallet.png deleted file mode 100644 index f41400593..000000000 Binary files a/en/img/blue/export-multisig-wallet.png and /dev/null differ diff --git a/en/img/blue/export-singlesig-wallet-125.png b/en/img/blue/export-singlesig-wallet-125.png deleted file mode 100644 index ca0f304b0..000000000 Binary files a/en/img/blue/export-singlesig-wallet-125.png and /dev/null differ diff --git a/en/img/blue/export-singlesig-wallet-150.png b/en/img/blue/export-singlesig-wallet-150.png deleted file mode 100644 index b374e3c75..000000000 Binary files a/en/img/blue/export-singlesig-wallet-150.png and /dev/null differ diff --git a/en/img/blue/export-singlesig-wallet-200.png b/en/img/blue/export-singlesig-wallet-200.png deleted file mode 100644 index 5475ac282..000000000 Binary files a/en/img/blue/export-singlesig-wallet-200.png and /dev/null differ diff --git a/en/img/blue/export-singlesig-wallet-400.png b/en/img/blue/export-singlesig-wallet-400.png deleted file mode 100644 index 4e3884aa1..000000000 Binary files a/en/img/blue/export-singlesig-wallet-400.png and /dev/null differ diff --git a/en/img/blue/export-singlesig-wallet-600.png b/en/img/blue/export-singlesig-wallet-600.png deleted file mode 100644 index a7fb861e8..000000000 Binary files a/en/img/blue/export-singlesig-wallet-600.png and /dev/null differ diff --git a/en/img/blue/export-singlesig-wallet.png b/en/img/blue/export-singlesig-wallet.png deleted file mode 100644 index 5b5822828..000000000 Binary files a/en/img/blue/export-singlesig-wallet.png and /dev/null differ diff --git a/en/img/blue/import-wallet-125.png b/en/img/blue/import-wallet-125.png deleted file mode 100644 index 89bf4544a..000000000 Binary files a/en/img/blue/import-wallet-125.png and /dev/null differ diff --git a/en/img/blue/import-wallet-150.png b/en/img/blue/import-wallet-150.png deleted file mode 100644 index f2364a178..000000000 Binary files a/en/img/blue/import-wallet-150.png and /dev/null differ diff --git a/en/img/blue/import-wallet-200.png b/en/img/blue/import-wallet-200.png deleted file mode 100644 index fb962dba2..000000000 Binary files a/en/img/blue/import-wallet-200.png and /dev/null differ diff --git a/en/img/blue/import-wallet-400.png b/en/img/blue/import-wallet-400.png deleted file mode 100644 index 71813993c..000000000 Binary files a/en/img/blue/import-wallet-400.png and /dev/null differ diff --git a/en/img/blue/import-wallet-600.png b/en/img/blue/import-wallet-600.png deleted file mode 100644 index c9de878ac..000000000 Binary files a/en/img/blue/import-wallet-600.png and /dev/null differ diff --git a/en/img/blue/import-wallet.png b/en/img/blue/import-wallet.png deleted file mode 100644 index baea760b8..000000000 Binary files a/en/img/blue/import-wallet.png and /dev/null differ diff --git a/en/img/blue/multisig-wallet-home-125.png b/en/img/blue/multisig-wallet-home-125.png deleted file mode 100644 index f1127960f..000000000 Binary files a/en/img/blue/multisig-wallet-home-125.png and /dev/null differ diff --git a/en/img/blue/multisig-wallet-home-150.png b/en/img/blue/multisig-wallet-home-150.png deleted file mode 100644 index 1f6572143..000000000 Binary files a/en/img/blue/multisig-wallet-home-150.png and /dev/null differ diff --git a/en/img/blue/multisig-wallet-home-200.png b/en/img/blue/multisig-wallet-home-200.png deleted file mode 100644 index 9b306bded..000000000 Binary files a/en/img/blue/multisig-wallet-home-200.png and /dev/null differ diff --git a/en/img/blue/multisig-wallet-home-400.png b/en/img/blue/multisig-wallet-home-400.png deleted file mode 100644 index 5a7e1811f..000000000 Binary files a/en/img/blue/multisig-wallet-home-400.png and /dev/null differ diff --git a/en/img/blue/multisig-wallet-home-600.png b/en/img/blue/multisig-wallet-home-600.png deleted file mode 100644 index 24b9d78df..000000000 Binary files a/en/img/blue/multisig-wallet-home-600.png and /dev/null differ diff --git a/en/img/blue/multisig-wallet-home.png b/en/img/blue/multisig-wallet-home.png deleted file mode 100644 index 1259d73c2..000000000 Binary files a/en/img/blue/multisig-wallet-home.png and /dev/null differ diff --git a/en/img/blue/new-vault-add-key-success-125.png b/en/img/blue/new-vault-add-key-success-125.png deleted file mode 100644 index 818c82a83..000000000 Binary files a/en/img/blue/new-vault-add-key-success-125.png and /dev/null differ diff --git a/en/img/blue/new-vault-add-key-success-150.png b/en/img/blue/new-vault-add-key-success-150.png deleted file mode 100644 index 18bacae7a..000000000 Binary files a/en/img/blue/new-vault-add-key-success-150.png and /dev/null differ diff --git a/en/img/blue/new-vault-add-key-success-200.png b/en/img/blue/new-vault-add-key-success-200.png deleted file mode 100644 index 2a1997332..000000000 Binary files a/en/img/blue/new-vault-add-key-success-200.png and /dev/null differ diff --git a/en/img/blue/new-vault-add-key-success-400.png b/en/img/blue/new-vault-add-key-success-400.png deleted file mode 100644 index 06b738c6a..000000000 Binary files a/en/img/blue/new-vault-add-key-success-400.png and /dev/null differ diff --git a/en/img/blue/new-vault-add-key-success-600.png b/en/img/blue/new-vault-add-key-success-600.png deleted file mode 100644 index f4b813c78..000000000 Binary files a/en/img/blue/new-vault-add-key-success-600.png and /dev/null differ diff --git a/en/img/blue/new-vault-add-key-success.png b/en/img/blue/new-vault-add-key-success.png deleted file mode 100644 index c4d4d7c68..000000000 Binary files a/en/img/blue/new-vault-add-key-success.png and /dev/null differ diff --git a/en/img/blue/new-vault-create-125.png b/en/img/blue/new-vault-create-125.png deleted file mode 100644 index 0c15c10c4..000000000 Binary files a/en/img/blue/new-vault-create-125.png and /dev/null differ diff --git a/en/img/blue/new-vault-create-150.png b/en/img/blue/new-vault-create-150.png deleted file mode 100644 index 956bb8b09..000000000 Binary files a/en/img/blue/new-vault-create-150.png and /dev/null differ diff --git a/en/img/blue/new-vault-create-200.png b/en/img/blue/new-vault-create-200.png deleted file mode 100644 index 064e8e394..000000000 Binary files a/en/img/blue/new-vault-create-200.png and /dev/null differ diff --git a/en/img/blue/new-vault-create-400.png b/en/img/blue/new-vault-create-400.png deleted file mode 100644 index cc75d345e..000000000 Binary files a/en/img/blue/new-vault-create-400.png and /dev/null differ diff --git a/en/img/blue/new-vault-create-600.png b/en/img/blue/new-vault-create-600.png deleted file mode 100644 index 3e4e183fe..000000000 Binary files a/en/img/blue/new-vault-create-600.png and /dev/null differ diff --git a/en/img/blue/new-vault-create.png b/en/img/blue/new-vault-create.png deleted file mode 100644 index 52fdff20b..000000000 Binary files a/en/img/blue/new-vault-create.png and /dev/null differ diff --git a/en/img/blue/new-vault-lets-start-125.png b/en/img/blue/new-vault-lets-start-125.png deleted file mode 100644 index 2975a15d2..000000000 Binary files a/en/img/blue/new-vault-lets-start-125.png and /dev/null differ diff --git a/en/img/blue/new-vault-lets-start-150.png b/en/img/blue/new-vault-lets-start-150.png deleted file mode 100644 index 0b76c1ca3..000000000 Binary files a/en/img/blue/new-vault-lets-start-150.png and /dev/null differ diff --git a/en/img/blue/new-vault-lets-start-200.png b/en/img/blue/new-vault-lets-start-200.png deleted file mode 100644 index 943663d26..000000000 Binary files a/en/img/blue/new-vault-lets-start-200.png and /dev/null differ diff --git a/en/img/blue/new-vault-lets-start-400.png b/en/img/blue/new-vault-lets-start-400.png deleted file mode 100644 index 4232a8960..000000000 Binary files a/en/img/blue/new-vault-lets-start-400.png and /dev/null differ diff --git a/en/img/blue/new-vault-lets-start-600.png b/en/img/blue/new-vault-lets-start-600.png deleted file mode 100644 index 69199a0ea..000000000 Binary files a/en/img/blue/new-vault-lets-start-600.png and /dev/null differ diff --git a/en/img/blue/new-vault-lets-start.png b/en/img/blue/new-vault-lets-start.png deleted file mode 100644 index fb23b38d6..000000000 Binary files a/en/img/blue/new-vault-lets-start.png and /dev/null differ diff --git a/en/img/blue/new-vault-scan-key-125.png b/en/img/blue/new-vault-scan-key-125.png deleted file mode 100644 index ce2b48759..000000000 Binary files a/en/img/blue/new-vault-scan-key-125.png and /dev/null differ diff --git a/en/img/blue/new-vault-scan-key-150.png b/en/img/blue/new-vault-scan-key-150.png deleted file mode 100644 index 0cb61280d..000000000 Binary files a/en/img/blue/new-vault-scan-key-150.png and /dev/null differ diff --git a/en/img/blue/new-vault-scan-key-200.png b/en/img/blue/new-vault-scan-key-200.png deleted file mode 100644 index 6d413edf2..000000000 Binary files a/en/img/blue/new-vault-scan-key-200.png and /dev/null differ diff --git a/en/img/blue/new-vault-scan-key-400.png b/en/img/blue/new-vault-scan-key-400.png deleted file mode 100644 index 22f7f26a1..000000000 Binary files a/en/img/blue/new-vault-scan-key-400.png and /dev/null differ diff --git a/en/img/blue/new-vault-scan-key-600.png b/en/img/blue/new-vault-scan-key-600.png deleted file mode 100644 index 29869e1c3..000000000 Binary files a/en/img/blue/new-vault-scan-key-600.png and /dev/null differ diff --git a/en/img/blue/new-vault-scan-key.png b/en/img/blue/new-vault-scan-key.png deleted file mode 100644 index 47b3bc7f2..000000000 Binary files a/en/img/blue/new-vault-scan-key.png and /dev/null differ diff --git a/en/img/blue/new-vault-settings-125.png b/en/img/blue/new-vault-settings-125.png deleted file mode 100644 index e197fa106..000000000 Binary files a/en/img/blue/new-vault-settings-125.png and /dev/null differ diff --git a/en/img/blue/new-vault-settings-150.png b/en/img/blue/new-vault-settings-150.png deleted file mode 100644 index bd8bc558f..000000000 Binary files a/en/img/blue/new-vault-settings-150.png and /dev/null differ diff --git a/en/img/blue/new-vault-settings-200.png b/en/img/blue/new-vault-settings-200.png deleted file mode 100644 index 95ec87b02..000000000 Binary files a/en/img/blue/new-vault-settings-200.png and /dev/null differ diff --git a/en/img/blue/new-vault-settings-400.png b/en/img/blue/new-vault-settings-400.png deleted file mode 100644 index f91180d35..000000000 Binary files a/en/img/blue/new-vault-settings-400.png and /dev/null differ diff --git a/en/img/blue/new-vault-settings-600.png b/en/img/blue/new-vault-settings-600.png deleted file mode 100644 index dea517748..000000000 Binary files a/en/img/blue/new-vault-settings-600.png and /dev/null differ diff --git a/en/img/blue/new-vault-settings.png b/en/img/blue/new-vault-settings.png deleted file mode 100644 index 7f96f891f..000000000 Binary files a/en/img/blue/new-vault-settings.png and /dev/null differ diff --git a/en/img/blue/receive-address-125.png b/en/img/blue/receive-address-125.png deleted file mode 100644 index 7b1bfd566..000000000 Binary files a/en/img/blue/receive-address-125.png and /dev/null differ diff --git a/en/img/blue/receive-address-150.png b/en/img/blue/receive-address-150.png deleted file mode 100644 index 9adebbe57..000000000 Binary files a/en/img/blue/receive-address-150.png and /dev/null differ diff --git a/en/img/blue/receive-address-200.png b/en/img/blue/receive-address-200.png deleted file mode 100644 index 5b7dbff7f..000000000 Binary files a/en/img/blue/receive-address-200.png and /dev/null differ diff --git a/en/img/blue/receive-address-400.png b/en/img/blue/receive-address-400.png deleted file mode 100644 index 7447d1bd9..000000000 Binary files a/en/img/blue/receive-address-400.png and /dev/null differ diff --git a/en/img/blue/receive-address-600.png b/en/img/blue/receive-address-600.png deleted file mode 100644 index fddee8db4..000000000 Binary files a/en/img/blue/receive-address-600.png and /dev/null differ diff --git a/en/img/blue/receive-address.png b/en/img/blue/receive-address.png deleted file mode 100644 index 5f75a9517..000000000 Binary files a/en/img/blue/receive-address.png and /dev/null differ diff --git a/en/img/blue/send-125.png b/en/img/blue/send-125.png deleted file mode 100644 index edf330d82..000000000 Binary files a/en/img/blue/send-125.png and /dev/null differ diff --git a/en/img/blue/send-150.png b/en/img/blue/send-150.png deleted file mode 100644 index 37ef7a51d..000000000 Binary files a/en/img/blue/send-150.png and /dev/null differ diff --git a/en/img/blue/send-200.png b/en/img/blue/send-200.png deleted file mode 100644 index 51ddf7c39..000000000 Binary files a/en/img/blue/send-200.png and /dev/null differ diff --git a/en/img/blue/send-400.png b/en/img/blue/send-400.png deleted file mode 100644 index ce023de39..000000000 Binary files a/en/img/blue/send-400.png and /dev/null differ diff --git a/en/img/blue/send-600.png b/en/img/blue/send-600.png deleted file mode 100644 index adfe8b506..000000000 Binary files a/en/img/blue/send-600.png and /dev/null differ diff --git a/en/img/blue/send.png b/en/img/blue/send.png deleted file mode 100644 index dd097a0b4..000000000 Binary files a/en/img/blue/send.png and /dev/null differ diff --git a/en/img/blue/singlesig-wallet-home-125.png b/en/img/blue/singlesig-wallet-home-125.png deleted file mode 100644 index 7cac7c262..000000000 Binary files a/en/img/blue/singlesig-wallet-home-125.png and /dev/null differ diff --git a/en/img/blue/singlesig-wallet-home-150.png b/en/img/blue/singlesig-wallet-home-150.png deleted file mode 100644 index 8bd152bb3..000000000 Binary files a/en/img/blue/singlesig-wallet-home-150.png and /dev/null differ diff --git a/en/img/blue/singlesig-wallet-home-200.png b/en/img/blue/singlesig-wallet-home-200.png deleted file mode 100644 index 65fcb0481..000000000 Binary files a/en/img/blue/singlesig-wallet-home-200.png and /dev/null differ diff --git a/en/img/blue/singlesig-wallet-home-400.png b/en/img/blue/singlesig-wallet-home-400.png deleted file mode 100644 index a5a4bf2df..000000000 Binary files a/en/img/blue/singlesig-wallet-home-400.png and /dev/null differ diff --git a/en/img/blue/singlesig-wallet-home-600.png b/en/img/blue/singlesig-wallet-home-600.png deleted file mode 100644 index ac2f35b86..000000000 Binary files a/en/img/blue/singlesig-wallet-home-600.png and /dev/null differ diff --git a/en/img/blue/singlesig-wallet-home.png b/en/img/blue/singlesig-wallet-home.png deleted file mode 100644 index cdb978d76..000000000 Binary files a/en/img/blue/singlesig-wallet-home.png and /dev/null differ diff --git a/en/img/favicon.png b/en/img/favicon.png deleted file mode 100644 index 4b6119071..000000000 Binary files a/en/img/favicon.png and /dev/null differ diff --git a/en/img/krux-devices.jpg b/en/img/krux-devices.jpg deleted file mode 100644 index f5ab12c39..000000000 Binary files a/en/img/krux-devices.jpg and /dev/null differ diff --git a/en/img/krux-installer/main.png b/en/img/krux-installer/main.png deleted file mode 100644 index cbadd5ef0..000000000 Binary files a/en/img/krux-installer/main.png and /dev/null differ diff --git a/en/img/krux-installer/main2.png b/en/img/krux-installer/main2.png deleted file mode 100644 index 319dc008c..000000000 Binary files a/en/img/krux-installer/main2.png and /dev/null differ diff --git a/en/img/krux-installer/select_device.png b/en/img/krux-installer/select_device.png deleted file mode 100644 index 3ee9527a4..000000000 Binary files a/en/img/krux-installer/select_device.png and /dev/null differ diff --git a/en/img/krux-installer/select_flash.png b/en/img/krux-installer/select_flash.png deleted file mode 100644 index 4bc6e457c..000000000 Binary files a/en/img/krux-installer/select_flash.png and /dev/null differ diff --git a/en/img/krux-installer/select_version_menu.png b/en/img/krux-installer/select_version_menu.png deleted file mode 100644 index 0e9a8acff..000000000 Binary files a/en/img/krux-installer/select_version_menu.png and /dev/null differ diff --git a/en/img/krux-installer/windows_warn0.jpg b/en/img/krux-installer/windows_warn0.jpg deleted file mode 100644 index e35a82030..000000000 Binary files a/en/img/krux-installer/windows_warn0.jpg and /dev/null differ diff --git a/en/img/krux-installer/wipe_warn.png b/en/img/krux-installer/wipe_warn.png deleted file mode 100644 index 83bf0ca41..000000000 Binary files a/en/img/krux-installer/wipe_warn.png and /dev/null differ diff --git a/en/img/maixpy_amigo/address-menu-150.png b/en/img/maixpy_amigo/address-menu-150.png deleted file mode 100644 index a611461af..000000000 Binary files a/en/img/maixpy_amigo/address-menu-150.png and /dev/null differ diff --git a/en/img/maixpy_amigo/backup-compact-qr-150.png b/en/img/maixpy_amigo/backup-compact-qr-150.png deleted file mode 100644 index d1c61d5b9..000000000 Binary files a/en/img/maixpy_amigo/backup-compact-qr-150.png and /dev/null differ diff --git a/en/img/maixpy_amigo/backup-mnemonic-numbers-150.png b/en/img/maixpy_amigo/backup-mnemonic-numbers-150.png deleted file mode 100644 index a75e0b887..000000000 Binary files a/en/img/maixpy_amigo/backup-mnemonic-numbers-150.png and /dev/null differ diff --git a/en/img/maixpy_amigo/backup-mnemonic-words-150.png b/en/img/maixpy_amigo/backup-mnemonic-words-150.png deleted file mode 100644 index 300c88a7c..000000000 Binary files a/en/img/maixpy_amigo/backup-mnemonic-words-150.png and /dev/null differ diff --git a/en/img/maixpy_amigo/backup-options-150.png b/en/img/maixpy_amigo/backup-options-150.png deleted file mode 100644 index 488d8ae1a..000000000 Binary files a/en/img/maixpy_amigo/backup-options-150.png and /dev/null differ diff --git a/en/img/maixpy_amigo/backup-qr-plain-text-150.png b/en/img/maixpy_amigo/backup-qr-plain-text-150.png deleted file mode 100644 index 6b4451be7..000000000 Binary files a/en/img/maixpy_amigo/backup-qr-plain-text-150.png and /dev/null differ diff --git a/en/img/maixpy_amigo/backup-seed-qr-150.png b/en/img/maixpy_amigo/backup-seed-qr-150.png deleted file mode 100644 index 7028eb1c5..000000000 Binary files a/en/img/maixpy_amigo/backup-seed-qr-150.png and /dev/null differ diff --git a/en/img/maixpy_amigo/backup-stackbit-150.png b/en/img/maixpy_amigo/backup-stackbit-150.png deleted file mode 100644 index 01152d9ce..000000000 Binary files a/en/img/maixpy_amigo/backup-stackbit-150.png and /dev/null differ diff --git a/en/img/maixpy_amigo/backup-tiny-seed-150.png b/en/img/maixpy_amigo/backup-tiny-seed-150.png deleted file mode 100644 index 708ba793f..000000000 Binary files a/en/img/maixpy_amigo/backup-tiny-seed-150.png and /dev/null differ diff --git a/en/img/maixpy_amigo/check-sd-card-150.png b/en/img/maixpy_amigo/check-sd-card-150.png deleted file mode 100644 index 8a5840182..000000000 Binary files a/en/img/maixpy_amigo/check-sd-card-150.png and /dev/null differ diff --git a/en/img/maixpy_amigo/create-qr-code-150.png b/en/img/maixpy_amigo/create-qr-code-150.png deleted file mode 100644 index f09281ae4..000000000 Binary files a/en/img/maixpy_amigo/create-qr-code-150.png and /dev/null differ diff --git a/en/img/maixpy_amigo/delete-mnemonic-150.png b/en/img/maixpy_amigo/delete-mnemonic-150.png deleted file mode 100644 index 0067fabe2..000000000 Binary files a/en/img/maixpy_amigo/delete-mnemonic-150.png and /dev/null differ diff --git a/en/img/maixpy_amigo/encryption-options-150.png b/en/img/maixpy_amigo/encryption-options-150.png deleted file mode 100644 index c6bd6285e..000000000 Binary files a/en/img/maixpy_amigo/encryption-options-150.png and /dev/null differ diff --git a/en/img/maixpy_amigo/encryption-options-mode-150.png b/en/img/maixpy_amigo/encryption-options-mode-150.png deleted file mode 100644 index 8857dd904..000000000 Binary files a/en/img/maixpy_amigo/encryption-options-mode-150.png and /dev/null differ diff --git a/en/img/maixpy_amigo/encryption-options-pbkdf2-150.png b/en/img/maixpy_amigo/encryption-options-pbkdf2-150.png deleted file mode 100644 index f28611eb8..000000000 Binary files a/en/img/maixpy_amigo/encryption-options-pbkdf2-150.png and /dev/null differ diff --git a/en/img/maixpy_amigo/extended-public-key-menu-150.png b/en/img/maixpy_amigo/extended-public-key-menu-150.png deleted file mode 100644 index 5019eeb42..000000000 Binary files a/en/img/maixpy_amigo/extended-public-key-menu-150.png and /dev/null differ diff --git a/en/img/maixpy_amigo/extended-public-key-wpkh-xpub-qr-150.png b/en/img/maixpy_amigo/extended-public-key-wpkh-xpub-qr-150.png deleted file mode 100644 index 527723970..000000000 Binary files a/en/img/maixpy_amigo/extended-public-key-wpkh-xpub-qr-150.png and /dev/null differ diff --git a/en/img/maixpy_amigo/extended-public-key-wpkh-xpub-text-150.png b/en/img/maixpy_amigo/extended-public-key-wpkh-xpub-text-150.png deleted file mode 100644 index 0a14a9e06..000000000 Binary files a/en/img/maixpy_amigo/extended-public-key-wpkh-xpub-text-150.png and /dev/null differ diff --git a/en/img/maixpy_amigo/extended-public-key-wpkh-zpub-qr-150.png b/en/img/maixpy_amigo/extended-public-key-wpkh-zpub-qr-150.png deleted file mode 100644 index 9b958c9be..000000000 Binary files a/en/img/maixpy_amigo/extended-public-key-wpkh-zpub-qr-150.png and /dev/null differ diff --git a/en/img/maixpy_amigo/extended-public-key-wsh-xpub-qr-150.png b/en/img/maixpy_amigo/extended-public-key-wsh-xpub-qr-150.png deleted file mode 100644 index ef55ef2ec..000000000 Binary files a/en/img/maixpy_amigo/extended-public-key-wsh-xpub-qr-150.png and /dev/null differ diff --git a/en/img/maixpy_amigo/extended-public-key-wsh-xpub-text-150.png b/en/img/maixpy_amigo/extended-public-key-wsh-xpub-text-150.png deleted file mode 100644 index 920758b18..000000000 Binary files a/en/img/maixpy_amigo/extended-public-key-wsh-xpub-text-150.png and /dev/null differ diff --git a/en/img/maixpy_amigo/grided-qr-code-150.png b/en/img/maixpy_amigo/grided-qr-code-150.png deleted file mode 100644 index 1a909150f..000000000 Binary files a/en/img/maixpy_amigo/grided-qr-code-150.png and /dev/null differ diff --git a/en/img/maixpy_amigo/home-encrypt-options-150.png b/en/img/maixpy_amigo/home-encrypt-options-150.png deleted file mode 100644 index d65326cce..000000000 Binary files a/en/img/maixpy_amigo/home-encrypt-options-150.png and /dev/null differ diff --git a/en/img/maixpy_amigo/home-options-150.png b/en/img/maixpy_amigo/home-options-150.png deleted file mode 100644 index b7d87a184..000000000 Binary files a/en/img/maixpy_amigo/home-options-150.png and /dev/null differ diff --git a/en/img/maixpy_amigo/lines-qr-code-150.png b/en/img/maixpy_amigo/lines-qr-code-150.png deleted file mode 100644 index d5abd6550..000000000 Binary files a/en/img/maixpy_amigo/lines-qr-code-150.png and /dev/null differ diff --git a/en/img/maixpy_amigo/list-address-change-150.png b/en/img/maixpy_amigo/list-address-change-150.png deleted file mode 100644 index d16c99e7d..000000000 Binary files a/en/img/maixpy_amigo/list-address-change-150.png and /dev/null differ diff --git a/en/img/maixpy_amigo/list-address-receive-150.png b/en/img/maixpy_amigo/list-address-receive-150.png deleted file mode 100644 index a5fff3a53..000000000 Binary files a/en/img/maixpy_amigo/list-address-receive-150.png and /dev/null differ diff --git a/en/img/maixpy_amigo/load-mnemonic-camera-options-150.png b/en/img/maixpy_amigo/load-mnemonic-camera-options-150.png deleted file mode 100644 index aa28f11fe..000000000 Binary files a/en/img/maixpy_amigo/load-mnemonic-camera-options-150.png and /dev/null differ diff --git a/en/img/maixpy_amigo/load-mnemonic-manual-options-150.png b/en/img/maixpy_amigo/load-mnemonic-manual-options-150.png deleted file mode 100644 index bff5907a6..000000000 Binary files a/en/img/maixpy_amigo/load-mnemonic-manual-options-150.png and /dev/null differ diff --git a/en/img/maixpy_amigo/load-mnemonic-options-150.png b/en/img/maixpy_amigo/load-mnemonic-options-150.png deleted file mode 100644 index 5398aff1f..000000000 Binary files a/en/img/maixpy_amigo/load-mnemonic-options-150.png and /dev/null differ diff --git a/en/img/maixpy_amigo/load-mnemonic-seq-fingerprint-150.png b/en/img/maixpy_amigo/load-mnemonic-seq-fingerprint-150.png deleted file mode 100644 index b0a01bafd..000000000 Binary files a/en/img/maixpy_amigo/load-mnemonic-seq-fingerprint-150.png and /dev/null differ diff --git a/en/img/maixpy_amigo/load-mnemonic-seq-mnemonic-150.png b/en/img/maixpy_amigo/load-mnemonic-seq-mnemonic-150.png deleted file mode 100644 index b96573619..000000000 Binary files a/en/img/maixpy_amigo/load-mnemonic-seq-mnemonic-150.png and /dev/null differ diff --git a/en/img/maixpy_amigo/load-mnemonic-seq-passphrase-150.png b/en/img/maixpy_amigo/load-mnemonic-seq-passphrase-150.png deleted file mode 100644 index e7ed8fe0c..000000000 Binary files a/en/img/maixpy_amigo/load-mnemonic-seq-passphrase-150.png and /dev/null differ diff --git a/en/img/maixpy_amigo/load-mnemonic-seq-single-multi-150.png b/en/img/maixpy_amigo/load-mnemonic-seq-single-multi-150.png deleted file mode 100644 index 418402560..000000000 Binary files a/en/img/maixpy_amigo/load-mnemonic-seq-single-multi-150.png and /dev/null differ diff --git a/en/img/maixpy_amigo/load-mnemonic-storage-options-150.png b/en/img/maixpy_amigo/load-mnemonic-storage-options-150.png deleted file mode 100644 index 0067fabe2..000000000 Binary files a/en/img/maixpy_amigo/load-mnemonic-storage-options-150.png and /dev/null differ diff --git a/en/img/maixpy_amigo/load-mnemonic-via-numbers-word-150.png b/en/img/maixpy_amigo/load-mnemonic-via-numbers-word-150.png deleted file mode 100644 index ae8a68cb2..000000000 Binary files a/en/img/maixpy_amigo/load-mnemonic-via-numbers-word-150.png and /dev/null differ diff --git a/en/img/maixpy_amigo/load-mnemonic-via-stackbit-filled-150.png b/en/img/maixpy_amigo/load-mnemonic-via-stackbit-filled-150.png deleted file mode 100644 index 293ddbac5..000000000 Binary files a/en/img/maixpy_amigo/load-mnemonic-via-stackbit-filled-150.png and /dev/null differ diff --git a/en/img/maixpy_amigo/load-mnemonic-via-text-word-150.png b/en/img/maixpy_amigo/load-mnemonic-via-text-word-150.png deleted file mode 100644 index cfec7ef42..000000000 Binary files a/en/img/maixpy_amigo/load-mnemonic-via-text-word-150.png and /dev/null differ diff --git a/en/img/maixpy_amigo/load-mnemonic-via-tinyseed-filled-150.png b/en/img/maixpy_amigo/load-mnemonic-via-tinyseed-filled-150.png deleted file mode 100644 index af4db79d6..000000000 Binary files a/en/img/maixpy_amigo/load-mnemonic-via-tinyseed-filled-150.png and /dev/null differ diff --git a/en/img/maixpy_amigo/locale-options-150.png b/en/img/maixpy_amigo/locale-options-150.png deleted file mode 100644 index 5bae84949..000000000 Binary files a/en/img/maixpy_amigo/locale-options-150.png and /dev/null differ diff --git a/en/img/maixpy_amigo/logo-150.png b/en/img/maixpy_amigo/logo-150.png deleted file mode 100644 index e3b211d10..000000000 Binary files a/en/img/maixpy_amigo/logo-150.png and /dev/null differ diff --git a/en/img/maixpy_amigo/network-options-150.png b/en/img/maixpy_amigo/network-options-150.png deleted file mode 100644 index b98453785..000000000 Binary files a/en/img/maixpy_amigo/network-options-150.png and /dev/null differ diff --git a/en/img/maixpy_amigo/new-mnemonic-options-150.png b/en/img/maixpy_amigo/new-mnemonic-options-150.png deleted file mode 100644 index 020ca6f56..000000000 Binary files a/en/img/maixpy_amigo/new-mnemonic-options-150.png and /dev/null differ diff --git a/en/img/maixpy_amigo/new-mnemonic-via-d20-last-n-rolls-150.png b/en/img/maixpy_amigo/new-mnemonic-via-d20-last-n-rolls-150.png deleted file mode 100644 index 69b0cecef..000000000 Binary files a/en/img/maixpy_amigo/new-mnemonic-via-d20-last-n-rolls-150.png and /dev/null differ diff --git a/en/img/maixpy_amigo/new-mnemonic-via-d20-roll-1-150.png b/en/img/maixpy_amigo/new-mnemonic-via-d20-roll-1-150.png deleted file mode 100644 index ac6a66a91..000000000 Binary files a/en/img/maixpy_amigo/new-mnemonic-via-d20-roll-1-150.png and /dev/null differ diff --git a/en/img/maixpy_amigo/new-mnemonic-via-d20-roll-nerd-stats-150.png b/en/img/maixpy_amigo/new-mnemonic-via-d20-roll-nerd-stats-150.png deleted file mode 100644 index b3df26cf6..000000000 Binary files a/en/img/maixpy_amigo/new-mnemonic-via-d20-roll-nerd-stats-150.png and /dev/null differ diff --git a/en/img/maixpy_amigo/new-mnemonic-via-d20-roll-sha256-150.png b/en/img/maixpy_amigo/new-mnemonic-via-d20-roll-sha256-150.png deleted file mode 100644 index 8393d9d6e..000000000 Binary files a/en/img/maixpy_amigo/new-mnemonic-via-d20-roll-sha256-150.png and /dev/null differ diff --git a/en/img/maixpy_amigo/new-mnemonic-via-d20-roll-string-150.png b/en/img/maixpy_amigo/new-mnemonic-via-d20-roll-string-150.png deleted file mode 100644 index caa7fce0d..000000000 Binary files a/en/img/maixpy_amigo/new-mnemonic-via-d20-roll-string-150.png and /dev/null differ diff --git a/en/img/maixpy_amigo/new-mnemonic-via-d6-last-n-rolls-150.png b/en/img/maixpy_amigo/new-mnemonic-via-d6-last-n-rolls-150.png deleted file mode 100644 index a3d274754..000000000 Binary files a/en/img/maixpy_amigo/new-mnemonic-via-d6-last-n-rolls-150.png and /dev/null differ diff --git a/en/img/maixpy_amigo/new-mnemonic-via-d6-roll-1-150.png b/en/img/maixpy_amigo/new-mnemonic-via-d6-roll-1-150.png deleted file mode 100644 index 74270adc9..000000000 Binary files a/en/img/maixpy_amigo/new-mnemonic-via-d6-roll-1-150.png and /dev/null differ diff --git a/en/img/maixpy_amigo/new-mnemonic-via-d6-roll-nerd-stats-150.png b/en/img/maixpy_amigo/new-mnemonic-via-d6-roll-nerd-stats-150.png deleted file mode 100644 index e551e4b48..000000000 Binary files a/en/img/maixpy_amigo/new-mnemonic-via-d6-roll-nerd-stats-150.png and /dev/null differ diff --git a/en/img/maixpy_amigo/new-mnemonic-via-d6-roll-sha256-150.png b/en/img/maixpy_amigo/new-mnemonic-via-d6-roll-sha256-150.png deleted file mode 100644 index 2f5a543c5..000000000 Binary files a/en/img/maixpy_amigo/new-mnemonic-via-d6-roll-sha256-150.png and /dev/null differ diff --git a/en/img/maixpy_amigo/new-mnemonic-via-d6-roll-string-150.png b/en/img/maixpy_amigo/new-mnemonic-via-d6-roll-string-150.png deleted file mode 100644 index 8644b615a..000000000 Binary files a/en/img/maixpy_amigo/new-mnemonic-via-d6-roll-string-150.png and /dev/null differ diff --git a/en/img/maixpy_amigo/new-mnemonic-via-snapshot-capturing-150.png b/en/img/maixpy_amigo/new-mnemonic-via-snapshot-capturing-150.png deleted file mode 100644 index 013c0f7c9..000000000 Binary files a/en/img/maixpy_amigo/new-mnemonic-via-snapshot-capturing-150.png and /dev/null differ diff --git a/en/img/maixpy_amigo/new-mnemonic-via-snapshot-entropy-estimation-150.png b/en/img/maixpy_amigo/new-mnemonic-via-snapshot-entropy-estimation-150.png deleted file mode 100644 index e2421c5f4..000000000 Binary files a/en/img/maixpy_amigo/new-mnemonic-via-snapshot-entropy-estimation-150.png and /dev/null differ diff --git a/en/img/maixpy_amigo/new-mnemonic-via-snapshot-prompt-150.png b/en/img/maixpy_amigo/new-mnemonic-via-snapshot-prompt-150.png deleted file mode 100644 index 25adb17b1..000000000 Binary files a/en/img/maixpy_amigo/new-mnemonic-via-snapshot-prompt-150.png and /dev/null differ diff --git a/en/img/maixpy_amigo/new-mnemonic-via-snapshot-sha256-150.png b/en/img/maixpy_amigo/new-mnemonic-via-snapshot-sha256-150.png deleted file mode 100644 index 22dc8f5c2..000000000 Binary files a/en/img/maixpy_amigo/new-mnemonic-via-snapshot-sha256-150.png and /dev/null differ diff --git a/en/img/maixpy_amigo/persist-options-150.png b/en/img/maixpy_amigo/persist-options-150.png deleted file mode 100644 index ecab5b9f6..000000000 Binary files a/en/img/maixpy_amigo/persist-options-150.png and /dev/null differ diff --git a/en/img/maixpy_amigo/print-qr-printing-150.png b/en/img/maixpy_amigo/print-qr-printing-150.png deleted file mode 100644 index eb28c3257..000000000 Binary files a/en/img/maixpy_amigo/print-qr-printing-150.png and /dev/null differ diff --git a/en/img/maixpy_amigo/print-qr-prompt-150.png b/en/img/maixpy_amigo/print-qr-prompt-150.png deleted file mode 100644 index 1b4377914..000000000 Binary files a/en/img/maixpy_amigo/print-qr-prompt-150.png and /dev/null differ diff --git a/en/img/maixpy_amigo/print-test-qr-150.png b/en/img/maixpy_amigo/print-test-qr-150.png deleted file mode 100644 index c5e607ba5..000000000 Binary files a/en/img/maixpy_amigo/print-test-qr-150.png and /dev/null differ diff --git a/en/img/maixpy_amigo/printer-options-150.png b/en/img/maixpy_amigo/printer-options-150.png deleted file mode 100644 index dd7edc65c..000000000 Binary files a/en/img/maixpy_amigo/printer-options-150.png and /dev/null differ diff --git a/en/img/maixpy_amigo/regions-qr-code-150.png b/en/img/maixpy_amigo/regions-qr-code-150.png deleted file mode 100644 index eda594772..000000000 Binary files a/en/img/maixpy_amigo/regions-qr-code-150.png and /dev/null differ diff --git a/en/img/maixpy_amigo/scan-address-scanned-address-150.png b/en/img/maixpy_amigo/scan-address-scanned-address-150.png deleted file mode 100644 index f48e26314..000000000 Binary files a/en/img/maixpy_amigo/scan-address-scanned-address-150.png and /dev/null differ diff --git a/en/img/maixpy_amigo/settings-options-150.png b/en/img/maixpy_amigo/settings-options-150.png deleted file mode 100644 index 3f2afe540..000000000 Binary files a/en/img/maixpy_amigo/settings-options-150.png and /dev/null differ diff --git a/en/img/maixpy_amigo/settings-options-appearance-150.png b/en/img/maixpy_amigo/settings-options-appearance-150.png deleted file mode 100644 index 94601c2a5..000000000 Binary files a/en/img/maixpy_amigo/settings-options-appearance-150.png and /dev/null differ diff --git a/en/img/maixpy_amigo/settings-options-appearance-screensaver-150.png b/en/img/maixpy_amigo/settings-options-appearance-screensaver-150.png deleted file mode 100644 index 7400dd425..000000000 Binary files a/en/img/maixpy_amigo/settings-options-appearance-screensaver-150.png and /dev/null differ diff --git a/en/img/maixpy_amigo/settings-options-factory-settings-150.png b/en/img/maixpy_amigo/settings-options-factory-settings-150.png deleted file mode 100644 index 07cc5d8ae..000000000 Binary files a/en/img/maixpy_amigo/settings-options-factory-settings-150.png and /dev/null differ diff --git a/en/img/maixpy_amigo/settings-options-hardware-150.png b/en/img/maixpy_amigo/settings-options-hardware-150.png deleted file mode 100644 index b0b98f94d..000000000 Binary files a/en/img/maixpy_amigo/settings-options-hardware-150.png and /dev/null differ diff --git a/en/img/maixpy_amigo/settings-options-hardware-display-150.png b/en/img/maixpy_amigo/settings-options-hardware-display-150.png deleted file mode 100644 index 1eef96349..000000000 Binary files a/en/img/maixpy_amigo/settings-options-hardware-display-150.png and /dev/null differ diff --git a/en/img/maixpy_amigo/sign-message-at-address-prompt-150.png b/en/img/maixpy_amigo/sign-message-at-address-prompt-150.png deleted file mode 100644 index e438c6962..000000000 Binary files a/en/img/maixpy_amigo/sign-message-at-address-prompt-150.png and /dev/null differ diff --git a/en/img/maixpy_amigo/sign-message-sha256-sign-prompt-150.png b/en/img/maixpy_amigo/sign-message-sha256-sign-prompt-150.png deleted file mode 100644 index c74d1c2f2..000000000 Binary files a/en/img/maixpy_amigo/sign-message-sha256-sign-prompt-150.png and /dev/null differ diff --git a/en/img/maixpy_amigo/sign-options-150.png b/en/img/maixpy_amigo/sign-options-150.png deleted file mode 100644 index f0afd1dd8..000000000 Binary files a/en/img/maixpy_amigo/sign-options-150.png and /dev/null differ diff --git a/en/img/maixpy_amigo/sign-psbt-sign-prompt-150.png b/en/img/maixpy_amigo/sign-psbt-sign-prompt-150.png deleted file mode 100644 index d982a9689..000000000 Binary files a/en/img/maixpy_amigo/sign-psbt-sign-prompt-150.png and /dev/null differ diff --git a/en/img/maixpy_amigo/sign-psbt-signed-qr-150.png b/en/img/maixpy_amigo/sign-psbt-signed-qr-150.png deleted file mode 100644 index 378b38c9d..000000000 Binary files a/en/img/maixpy_amigo/sign-psbt-signed-qr-150.png and /dev/null differ diff --git a/en/img/maixpy_amigo/standard-qr-code-150.png b/en/img/maixpy_amigo/standard-qr-code-150.png deleted file mode 100644 index e99a37770..000000000 Binary files a/en/img/maixpy_amigo/standard-qr-code-150.png and /dev/null differ diff --git a/en/img/maixpy_amigo/theme-1-150.png b/en/img/maixpy_amigo/theme-1-150.png deleted file mode 100644 index 7ee9d764d..000000000 Binary files a/en/img/maixpy_amigo/theme-1-150.png and /dev/null differ diff --git a/en/img/maixpy_amigo/theme-2-150.png b/en/img/maixpy_amigo/theme-2-150.png deleted file mode 100644 index bb106d8bc..000000000 Binary files a/en/img/maixpy_amigo/theme-2-150.png and /dev/null differ diff --git a/en/img/maixpy_amigo/theme-3-150.png b/en/img/maixpy_amigo/theme-3-150.png deleted file mode 100644 index 54a72f23b..000000000 Binary files a/en/img/maixpy_amigo/theme-3-150.png and /dev/null differ diff --git a/en/img/maixpy_amigo/theme-4-150.png b/en/img/maixpy_amigo/theme-4-150.png deleted file mode 100644 index 92f019e4b..000000000 Binary files a/en/img/maixpy_amigo/theme-4-150.png and /dev/null differ diff --git a/en/img/maixpy_amigo/theme-5-150.png b/en/img/maixpy_amigo/theme-5-150.png deleted file mode 100644 index 9512577e7..000000000 Binary files a/en/img/maixpy_amigo/theme-5-150.png and /dev/null differ diff --git a/en/img/maixpy_amigo/tools-options-150.png b/en/img/maixpy_amigo/tools-options-150.png deleted file mode 100644 index 69e944dd0..000000000 Binary files a/en/img/maixpy_amigo/tools-options-150.png and /dev/null differ diff --git a/en/img/maixpy_amigo/touchscreen-150.png b/en/img/maixpy_amigo/touchscreen-150.png deleted file mode 100644 index a130804e4..000000000 Binary files a/en/img/maixpy_amigo/touchscreen-150.png and /dev/null differ diff --git a/en/img/maixpy_amigo/wallet-load-prompt-150.png b/en/img/maixpy_amigo/wallet-load-prompt-150.png deleted file mode 100644 index 8b2436287..000000000 Binary files a/en/img/maixpy_amigo/wallet-load-prompt-150.png and /dev/null differ diff --git a/en/img/maixpy_amigo/wallet-type-options-150.png b/en/img/maixpy_amigo/wallet-type-options-150.png deleted file mode 100644 index 418402560..000000000 Binary files a/en/img/maixpy_amigo/wallet-type-options-150.png and /dev/null differ diff --git a/en/img/maixpy_amigo/wallet-type-options-multisig-150.png b/en/img/maixpy_amigo/wallet-type-options-multisig-150.png deleted file mode 100644 index 1ee564010..000000000 Binary files a/en/img/maixpy_amigo/wallet-type-options-multisig-150.png and /dev/null differ diff --git a/en/img/maixpy_amigo/wallet-wpkh-load-prompt-150.png b/en/img/maixpy_amigo/wallet-wpkh-load-prompt-150.png deleted file mode 100644 index 182b33be6..000000000 Binary files a/en/img/maixpy_amigo/wallet-wpkh-load-prompt-150.png and /dev/null differ diff --git a/en/img/maixpy_amigo/wallet-wsh-load-prompt-fingerprints-150.png b/en/img/maixpy_amigo/wallet-wsh-load-prompt-fingerprints-150.png deleted file mode 100644 index 8b6b3f068..000000000 Binary files a/en/img/maixpy_amigo/wallet-wsh-load-prompt-fingerprints-150.png and /dev/null differ diff --git a/en/img/maixpy_amigo/wallet-wsh-load-prompt-xpubs-150.png b/en/img/maixpy_amigo/wallet-wsh-load-prompt-xpubs-150.png deleted file mode 100644 index afda092b1..000000000 Binary files a/en/img/maixpy_amigo/wallet-wsh-load-prompt-xpubs-150.png and /dev/null differ diff --git a/en/img/maixpy_amigo/wipe-device-150.png b/en/img/maixpy_amigo/wipe-device-150.png deleted file mode 100644 index bd9dd9a29..000000000 Binary files a/en/img/maixpy_amigo/wipe-device-150.png and /dev/null differ diff --git a/en/img/maixpy_amigo/zoomed-qr-code-150.png b/en/img/maixpy_amigo/zoomed-qr-code-150.png deleted file mode 100644 index 2efe23eb8..000000000 Binary files a/en/img/maixpy_amigo/zoomed-qr-code-150.png and /dev/null differ diff --git a/en/img/maixpy_dock/logo-151.png b/en/img/maixpy_dock/logo-151.png deleted file mode 100644 index dbba9ea68..000000000 Binary files a/en/img/maixpy_dock/logo-151.png and /dev/null differ diff --git a/en/img/maixpy_m5stickv/address-menu-125.png b/en/img/maixpy_m5stickv/address-menu-125.png deleted file mode 100644 index 5084c0045..000000000 Binary files a/en/img/maixpy_m5stickv/address-menu-125.png and /dev/null differ diff --git a/en/img/maixpy_m5stickv/backup-compact-qr-125.png b/en/img/maixpy_m5stickv/backup-compact-qr-125.png deleted file mode 100644 index 4a67c5f2e..000000000 Binary files a/en/img/maixpy_m5stickv/backup-compact-qr-125.png and /dev/null differ diff --git a/en/img/maixpy_m5stickv/backup-mnemonic-numbers-125.png b/en/img/maixpy_m5stickv/backup-mnemonic-numbers-125.png deleted file mode 100644 index 9a01b64ef..000000000 Binary files a/en/img/maixpy_m5stickv/backup-mnemonic-numbers-125.png and /dev/null differ diff --git a/en/img/maixpy_m5stickv/backup-mnemonic-words-125.png b/en/img/maixpy_m5stickv/backup-mnemonic-words-125.png deleted file mode 100644 index 517c1065c..000000000 Binary files a/en/img/maixpy_m5stickv/backup-mnemonic-words-125.png and /dev/null differ diff --git a/en/img/maixpy_m5stickv/backup-options-125.png b/en/img/maixpy_m5stickv/backup-options-125.png deleted file mode 100644 index c069cf230..000000000 Binary files a/en/img/maixpy_m5stickv/backup-options-125.png and /dev/null differ diff --git a/en/img/maixpy_m5stickv/backup-qr-plain-text-125.png b/en/img/maixpy_m5stickv/backup-qr-plain-text-125.png deleted file mode 100644 index 9cc32a052..000000000 Binary files a/en/img/maixpy_m5stickv/backup-qr-plain-text-125.png and /dev/null differ diff --git a/en/img/maixpy_m5stickv/backup-seed-qr-125.png b/en/img/maixpy_m5stickv/backup-seed-qr-125.png deleted file mode 100644 index a91226e69..000000000 Binary files a/en/img/maixpy_m5stickv/backup-seed-qr-125.png and /dev/null differ diff --git a/en/img/maixpy_m5stickv/backup-stackbit-125.png b/en/img/maixpy_m5stickv/backup-stackbit-125.png deleted file mode 100644 index 740f5ace5..000000000 Binary files a/en/img/maixpy_m5stickv/backup-stackbit-125.png and /dev/null differ diff --git a/en/img/maixpy_m5stickv/backup-tiny-seed-125.png b/en/img/maixpy_m5stickv/backup-tiny-seed-125.png deleted file mode 100644 index 21050d9d1..000000000 Binary files a/en/img/maixpy_m5stickv/backup-tiny-seed-125.png and /dev/null differ diff --git a/en/img/maixpy_m5stickv/check-sd-card-125.png b/en/img/maixpy_m5stickv/check-sd-card-125.png deleted file mode 100644 index 1a3682507..000000000 Binary files a/en/img/maixpy_m5stickv/check-sd-card-125.png and /dev/null differ diff --git a/en/img/maixpy_m5stickv/create-qr-code-125.png b/en/img/maixpy_m5stickv/create-qr-code-125.png deleted file mode 100644 index a9c979754..000000000 Binary files a/en/img/maixpy_m5stickv/create-qr-code-125.png and /dev/null differ diff --git a/en/img/maixpy_m5stickv/delete-mnemonic-125.png b/en/img/maixpy_m5stickv/delete-mnemonic-125.png deleted file mode 100644 index dd9b8c573..000000000 Binary files a/en/img/maixpy_m5stickv/delete-mnemonic-125.png and /dev/null differ diff --git a/en/img/maixpy_m5stickv/encryption-options-125.png b/en/img/maixpy_m5stickv/encryption-options-125.png deleted file mode 100644 index ed8454aef..000000000 Binary files a/en/img/maixpy_m5stickv/encryption-options-125.png and /dev/null differ diff --git a/en/img/maixpy_m5stickv/encryption-options-mode-125.png b/en/img/maixpy_m5stickv/encryption-options-mode-125.png deleted file mode 100644 index 71451ed49..000000000 Binary files a/en/img/maixpy_m5stickv/encryption-options-mode-125.png and /dev/null differ diff --git a/en/img/maixpy_m5stickv/encryption-options-pbkdf2-125.png b/en/img/maixpy_m5stickv/encryption-options-pbkdf2-125.png deleted file mode 100644 index 762ccbed8..000000000 Binary files a/en/img/maixpy_m5stickv/encryption-options-pbkdf2-125.png and /dev/null differ diff --git a/en/img/maixpy_m5stickv/extended-public-key-menu-125.png b/en/img/maixpy_m5stickv/extended-public-key-menu-125.png deleted file mode 100644 index 206132966..000000000 Binary files a/en/img/maixpy_m5stickv/extended-public-key-menu-125.png and /dev/null differ diff --git a/en/img/maixpy_m5stickv/extended-public-key-wpkh-xpub-qr-125.png b/en/img/maixpy_m5stickv/extended-public-key-wpkh-xpub-qr-125.png deleted file mode 100644 index 734cd07d2..000000000 Binary files a/en/img/maixpy_m5stickv/extended-public-key-wpkh-xpub-qr-125.png and /dev/null differ diff --git a/en/img/maixpy_m5stickv/extended-public-key-wpkh-xpub-text-125.png b/en/img/maixpy_m5stickv/extended-public-key-wpkh-xpub-text-125.png deleted file mode 100644 index 6e4f3ab52..000000000 Binary files a/en/img/maixpy_m5stickv/extended-public-key-wpkh-xpub-text-125.png and /dev/null differ diff --git a/en/img/maixpy_m5stickv/extended-public-key-wpkh-zpub-qr-125.png b/en/img/maixpy_m5stickv/extended-public-key-wpkh-zpub-qr-125.png deleted file mode 100644 index 799eb6882..000000000 Binary files a/en/img/maixpy_m5stickv/extended-public-key-wpkh-zpub-qr-125.png and /dev/null differ diff --git a/en/img/maixpy_m5stickv/extended-public-key-wsh-xpub-qr-125.png b/en/img/maixpy_m5stickv/extended-public-key-wsh-xpub-qr-125.png deleted file mode 100644 index 877b88119..000000000 Binary files a/en/img/maixpy_m5stickv/extended-public-key-wsh-xpub-qr-125.png and /dev/null differ diff --git a/en/img/maixpy_m5stickv/extended-public-key-wsh-xpub-text-125.png b/en/img/maixpy_m5stickv/extended-public-key-wsh-xpub-text-125.png deleted file mode 100644 index 0cf3ca547..000000000 Binary files a/en/img/maixpy_m5stickv/extended-public-key-wsh-xpub-text-125.png and /dev/null differ diff --git a/en/img/maixpy_m5stickv/grided-qr-code-125.png b/en/img/maixpy_m5stickv/grided-qr-code-125.png deleted file mode 100644 index 9a3f1a414..000000000 Binary files a/en/img/maixpy_m5stickv/grided-qr-code-125.png and /dev/null differ diff --git a/en/img/maixpy_m5stickv/home-encrypt-options-125.png b/en/img/maixpy_m5stickv/home-encrypt-options-125.png deleted file mode 100644 index 579a7472d..000000000 Binary files a/en/img/maixpy_m5stickv/home-encrypt-options-125.png and /dev/null differ diff --git a/en/img/maixpy_m5stickv/home-options-125.png b/en/img/maixpy_m5stickv/home-options-125.png deleted file mode 100644 index dc3f3c9c9..000000000 Binary files a/en/img/maixpy_m5stickv/home-options-125.png and /dev/null differ diff --git a/en/img/maixpy_m5stickv/lines-qr-code-125.png b/en/img/maixpy_m5stickv/lines-qr-code-125.png deleted file mode 100644 index 7f59dfa3c..000000000 Binary files a/en/img/maixpy_m5stickv/lines-qr-code-125.png and /dev/null differ diff --git a/en/img/maixpy_m5stickv/list-address-change-125.png b/en/img/maixpy_m5stickv/list-address-change-125.png deleted file mode 100644 index b3683ea92..000000000 Binary files a/en/img/maixpy_m5stickv/list-address-change-125.png and /dev/null differ diff --git a/en/img/maixpy_m5stickv/list-address-receive-125.png b/en/img/maixpy_m5stickv/list-address-receive-125.png deleted file mode 100644 index 80ea311f6..000000000 Binary files a/en/img/maixpy_m5stickv/list-address-receive-125.png and /dev/null differ diff --git a/en/img/maixpy_m5stickv/load-mnemonic-camera-options-125.png b/en/img/maixpy_m5stickv/load-mnemonic-camera-options-125.png deleted file mode 100644 index 7455a5981..000000000 Binary files a/en/img/maixpy_m5stickv/load-mnemonic-camera-options-125.png and /dev/null differ diff --git a/en/img/maixpy_m5stickv/load-mnemonic-manual-options-125.png b/en/img/maixpy_m5stickv/load-mnemonic-manual-options-125.png deleted file mode 100644 index c4dcf672a..000000000 Binary files a/en/img/maixpy_m5stickv/load-mnemonic-manual-options-125.png and /dev/null differ diff --git a/en/img/maixpy_m5stickv/load-mnemonic-options-125.png b/en/img/maixpy_m5stickv/load-mnemonic-options-125.png deleted file mode 100644 index 4b5b582ba..000000000 Binary files a/en/img/maixpy_m5stickv/load-mnemonic-options-125.png and /dev/null differ diff --git a/en/img/maixpy_m5stickv/load-mnemonic-seq-fingerprint-125.png b/en/img/maixpy_m5stickv/load-mnemonic-seq-fingerprint-125.png deleted file mode 100644 index f418379a6..000000000 Binary files a/en/img/maixpy_m5stickv/load-mnemonic-seq-fingerprint-125.png and /dev/null differ diff --git a/en/img/maixpy_m5stickv/load-mnemonic-seq-mnemonic-125.png b/en/img/maixpy_m5stickv/load-mnemonic-seq-mnemonic-125.png deleted file mode 100644 index 2f953618f..000000000 Binary files a/en/img/maixpy_m5stickv/load-mnemonic-seq-mnemonic-125.png and /dev/null differ diff --git a/en/img/maixpy_m5stickv/load-mnemonic-seq-passphrase-125.png b/en/img/maixpy_m5stickv/load-mnemonic-seq-passphrase-125.png deleted file mode 100644 index 46f6cff11..000000000 Binary files a/en/img/maixpy_m5stickv/load-mnemonic-seq-passphrase-125.png and /dev/null differ diff --git a/en/img/maixpy_m5stickv/load-mnemonic-seq-single-multi-125.png b/en/img/maixpy_m5stickv/load-mnemonic-seq-single-multi-125.png deleted file mode 100644 index 02874ffa5..000000000 Binary files a/en/img/maixpy_m5stickv/load-mnemonic-seq-single-multi-125.png and /dev/null differ diff --git a/en/img/maixpy_m5stickv/load-mnemonic-storage-options-125.png b/en/img/maixpy_m5stickv/load-mnemonic-storage-options-125.png deleted file mode 100644 index dd9b8c573..000000000 Binary files a/en/img/maixpy_m5stickv/load-mnemonic-storage-options-125.png and /dev/null differ diff --git a/en/img/maixpy_m5stickv/load-mnemonic-via-numbers-word-125.png b/en/img/maixpy_m5stickv/load-mnemonic-via-numbers-word-125.png deleted file mode 100644 index 71c5c583c..000000000 Binary files a/en/img/maixpy_m5stickv/load-mnemonic-via-numbers-word-125.png and /dev/null differ diff --git a/en/img/maixpy_m5stickv/load-mnemonic-via-stackbit-filled-125.png b/en/img/maixpy_m5stickv/load-mnemonic-via-stackbit-filled-125.png deleted file mode 100644 index ec5019913..000000000 Binary files a/en/img/maixpy_m5stickv/load-mnemonic-via-stackbit-filled-125.png and /dev/null differ diff --git a/en/img/maixpy_m5stickv/load-mnemonic-via-text-word-125.png b/en/img/maixpy_m5stickv/load-mnemonic-via-text-word-125.png deleted file mode 100644 index 6f9675c94..000000000 Binary files a/en/img/maixpy_m5stickv/load-mnemonic-via-text-word-125.png and /dev/null differ diff --git a/en/img/maixpy_m5stickv/load-mnemonic-via-tinyseed-filled-125.png b/en/img/maixpy_m5stickv/load-mnemonic-via-tinyseed-filled-125.png deleted file mode 100644 index 5d7b359f0..000000000 Binary files a/en/img/maixpy_m5stickv/load-mnemonic-via-tinyseed-filled-125.png and /dev/null differ diff --git a/en/img/maixpy_m5stickv/locale-options-125.png b/en/img/maixpy_m5stickv/locale-options-125.png deleted file mode 100644 index e273b084a..000000000 Binary files a/en/img/maixpy_m5stickv/locale-options-125.png and /dev/null differ diff --git a/en/img/maixpy_m5stickv/logo-125.png b/en/img/maixpy_m5stickv/logo-125.png deleted file mode 100644 index dd6883946..000000000 Binary files a/en/img/maixpy_m5stickv/logo-125.png and /dev/null differ diff --git a/en/img/maixpy_m5stickv/network-options-125.png b/en/img/maixpy_m5stickv/network-options-125.png deleted file mode 100644 index e88264447..000000000 Binary files a/en/img/maixpy_m5stickv/network-options-125.png and /dev/null differ diff --git a/en/img/maixpy_m5stickv/new-mnemonic-options-125.png b/en/img/maixpy_m5stickv/new-mnemonic-options-125.png deleted file mode 100644 index 7f5de3cd4..000000000 Binary files a/en/img/maixpy_m5stickv/new-mnemonic-options-125.png and /dev/null differ diff --git a/en/img/maixpy_m5stickv/new-mnemonic-via-d20-last-n-rolls-125.png b/en/img/maixpy_m5stickv/new-mnemonic-via-d20-last-n-rolls-125.png deleted file mode 100644 index 461b21ce9..000000000 Binary files a/en/img/maixpy_m5stickv/new-mnemonic-via-d20-last-n-rolls-125.png and /dev/null differ diff --git a/en/img/maixpy_m5stickv/new-mnemonic-via-d20-roll-1-125.png b/en/img/maixpy_m5stickv/new-mnemonic-via-d20-roll-1-125.png deleted file mode 100644 index 0c582b238..000000000 Binary files a/en/img/maixpy_m5stickv/new-mnemonic-via-d20-roll-1-125.png and /dev/null differ diff --git a/en/img/maixpy_m5stickv/new-mnemonic-via-d20-roll-nerd-stats-125.png b/en/img/maixpy_m5stickv/new-mnemonic-via-d20-roll-nerd-stats-125.png deleted file mode 100644 index 0e2beb7d0..000000000 Binary files a/en/img/maixpy_m5stickv/new-mnemonic-via-d20-roll-nerd-stats-125.png and /dev/null differ diff --git a/en/img/maixpy_m5stickv/new-mnemonic-via-d20-roll-sha256-125.png b/en/img/maixpy_m5stickv/new-mnemonic-via-d20-roll-sha256-125.png deleted file mode 100644 index eee35b844..000000000 Binary files a/en/img/maixpy_m5stickv/new-mnemonic-via-d20-roll-sha256-125.png and /dev/null differ diff --git a/en/img/maixpy_m5stickv/new-mnemonic-via-d20-roll-string-125.png b/en/img/maixpy_m5stickv/new-mnemonic-via-d20-roll-string-125.png deleted file mode 100644 index 9a59be8c2..000000000 Binary files a/en/img/maixpy_m5stickv/new-mnemonic-via-d20-roll-string-125.png and /dev/null differ diff --git a/en/img/maixpy_m5stickv/new-mnemonic-via-d6-last-n-rolls-125.png b/en/img/maixpy_m5stickv/new-mnemonic-via-d6-last-n-rolls-125.png deleted file mode 100644 index 9b4f168d1..000000000 Binary files a/en/img/maixpy_m5stickv/new-mnemonic-via-d6-last-n-rolls-125.png and /dev/null differ diff --git a/en/img/maixpy_m5stickv/new-mnemonic-via-d6-roll-1-125.png b/en/img/maixpy_m5stickv/new-mnemonic-via-d6-roll-1-125.png deleted file mode 100644 index 713b5a14a..000000000 Binary files a/en/img/maixpy_m5stickv/new-mnemonic-via-d6-roll-1-125.png and /dev/null differ diff --git a/en/img/maixpy_m5stickv/new-mnemonic-via-d6-roll-nerd-stats-125.png b/en/img/maixpy_m5stickv/new-mnemonic-via-d6-roll-nerd-stats-125.png deleted file mode 100644 index 463aa2d40..000000000 Binary files a/en/img/maixpy_m5stickv/new-mnemonic-via-d6-roll-nerd-stats-125.png and /dev/null differ diff --git a/en/img/maixpy_m5stickv/new-mnemonic-via-d6-roll-sha256-125.png b/en/img/maixpy_m5stickv/new-mnemonic-via-d6-roll-sha256-125.png deleted file mode 100644 index 8f4c93469..000000000 Binary files a/en/img/maixpy_m5stickv/new-mnemonic-via-d6-roll-sha256-125.png and /dev/null differ diff --git a/en/img/maixpy_m5stickv/new-mnemonic-via-d6-roll-string-125.png b/en/img/maixpy_m5stickv/new-mnemonic-via-d6-roll-string-125.png deleted file mode 100644 index 3737c596e..000000000 Binary files a/en/img/maixpy_m5stickv/new-mnemonic-via-d6-roll-string-125.png and /dev/null differ diff --git a/en/img/maixpy_m5stickv/new-mnemonic-via-snapshot-capturing-125.png b/en/img/maixpy_m5stickv/new-mnemonic-via-snapshot-capturing-125.png deleted file mode 100644 index e45432dcd..000000000 Binary files a/en/img/maixpy_m5stickv/new-mnemonic-via-snapshot-capturing-125.png and /dev/null differ diff --git a/en/img/maixpy_m5stickv/new-mnemonic-via-snapshot-entropy-estimation-125.png b/en/img/maixpy_m5stickv/new-mnemonic-via-snapshot-entropy-estimation-125.png deleted file mode 100644 index 3b9f16e06..000000000 Binary files a/en/img/maixpy_m5stickv/new-mnemonic-via-snapshot-entropy-estimation-125.png and /dev/null differ diff --git a/en/img/maixpy_m5stickv/new-mnemonic-via-snapshot-prompt-125.png b/en/img/maixpy_m5stickv/new-mnemonic-via-snapshot-prompt-125.png deleted file mode 100644 index d334bc84c..000000000 Binary files a/en/img/maixpy_m5stickv/new-mnemonic-via-snapshot-prompt-125.png and /dev/null differ diff --git a/en/img/maixpy_m5stickv/new-mnemonic-via-snapshot-sha256-125.png b/en/img/maixpy_m5stickv/new-mnemonic-via-snapshot-sha256-125.png deleted file mode 100644 index 2eb839d72..000000000 Binary files a/en/img/maixpy_m5stickv/new-mnemonic-via-snapshot-sha256-125.png and /dev/null differ diff --git a/en/img/maixpy_m5stickv/persist-options-125.png b/en/img/maixpy_m5stickv/persist-options-125.png deleted file mode 100644 index fb461b0d6..000000000 Binary files a/en/img/maixpy_m5stickv/persist-options-125.png and /dev/null differ diff --git a/en/img/maixpy_m5stickv/print-qr-printing-125.png b/en/img/maixpy_m5stickv/print-qr-printing-125.png deleted file mode 100644 index 7c371b1c1..000000000 Binary files a/en/img/maixpy_m5stickv/print-qr-printing-125.png and /dev/null differ diff --git a/en/img/maixpy_m5stickv/print-qr-prompt-125.png b/en/img/maixpy_m5stickv/print-qr-prompt-125.png deleted file mode 100644 index 4e6f49a38..000000000 Binary files a/en/img/maixpy_m5stickv/print-qr-prompt-125.png and /dev/null differ diff --git a/en/img/maixpy_m5stickv/print-test-qr-125.png b/en/img/maixpy_m5stickv/print-test-qr-125.png deleted file mode 100644 index 110731c9e..000000000 Binary files a/en/img/maixpy_m5stickv/print-test-qr-125.png and /dev/null differ diff --git a/en/img/maixpy_m5stickv/printer-options-125.png b/en/img/maixpy_m5stickv/printer-options-125.png deleted file mode 100644 index 5650a5a4b..000000000 Binary files a/en/img/maixpy_m5stickv/printer-options-125.png and /dev/null differ diff --git a/en/img/maixpy_m5stickv/regions-qr-code-125.png b/en/img/maixpy_m5stickv/regions-qr-code-125.png deleted file mode 100644 index 33aeec3d8..000000000 Binary files a/en/img/maixpy_m5stickv/regions-qr-code-125.png and /dev/null differ diff --git a/en/img/maixpy_m5stickv/scan-address-scanned-address-125.png b/en/img/maixpy_m5stickv/scan-address-scanned-address-125.png deleted file mode 100644 index 110ca9b56..000000000 Binary files a/en/img/maixpy_m5stickv/scan-address-scanned-address-125.png and /dev/null differ diff --git a/en/img/maixpy_m5stickv/settings-options-125.png b/en/img/maixpy_m5stickv/settings-options-125.png deleted file mode 100644 index c896a583f..000000000 Binary files a/en/img/maixpy_m5stickv/settings-options-125.png and /dev/null differ diff --git a/en/img/maixpy_m5stickv/settings-options-appearance-125.png b/en/img/maixpy_m5stickv/settings-options-appearance-125.png deleted file mode 100644 index 53845b050..000000000 Binary files a/en/img/maixpy_m5stickv/settings-options-appearance-125.png and /dev/null differ diff --git a/en/img/maixpy_m5stickv/settings-options-appearance-screensaver-125.png b/en/img/maixpy_m5stickv/settings-options-appearance-screensaver-125.png deleted file mode 100644 index 9707859bd..000000000 Binary files a/en/img/maixpy_m5stickv/settings-options-appearance-screensaver-125.png and /dev/null differ diff --git a/en/img/maixpy_m5stickv/settings-options-factory-settings-125.png b/en/img/maixpy_m5stickv/settings-options-factory-settings-125.png deleted file mode 100644 index af3bae249..000000000 Binary files a/en/img/maixpy_m5stickv/settings-options-factory-settings-125.png and /dev/null differ diff --git a/en/img/maixpy_m5stickv/settings-options-hardware-125.png b/en/img/maixpy_m5stickv/settings-options-hardware-125.png deleted file mode 100644 index 5650a5a4b..000000000 Binary files a/en/img/maixpy_m5stickv/settings-options-hardware-125.png and /dev/null differ diff --git a/en/img/maixpy_m5stickv/settings-options-hardware-display-125.png b/en/img/maixpy_m5stickv/settings-options-hardware-display-125.png deleted file mode 100644 index 5650a5a4b..000000000 Binary files a/en/img/maixpy_m5stickv/settings-options-hardware-display-125.png and /dev/null differ diff --git a/en/img/maixpy_m5stickv/sign-message-at-address-prompt-125.png b/en/img/maixpy_m5stickv/sign-message-at-address-prompt-125.png deleted file mode 100644 index 89fc0d628..000000000 Binary files a/en/img/maixpy_m5stickv/sign-message-at-address-prompt-125.png and /dev/null differ diff --git a/en/img/maixpy_m5stickv/sign-message-sha256-sign-prompt-125.png b/en/img/maixpy_m5stickv/sign-message-sha256-sign-prompt-125.png deleted file mode 100644 index 1a40a0e12..000000000 Binary files a/en/img/maixpy_m5stickv/sign-message-sha256-sign-prompt-125.png and /dev/null differ diff --git a/en/img/maixpy_m5stickv/sign-options-125.png b/en/img/maixpy_m5stickv/sign-options-125.png deleted file mode 100644 index 300b6af21..000000000 Binary files a/en/img/maixpy_m5stickv/sign-options-125.png and /dev/null differ diff --git a/en/img/maixpy_m5stickv/sign-psbt-sign-prompt-125.png b/en/img/maixpy_m5stickv/sign-psbt-sign-prompt-125.png deleted file mode 100644 index afffade9a..000000000 Binary files a/en/img/maixpy_m5stickv/sign-psbt-sign-prompt-125.png and /dev/null differ diff --git a/en/img/maixpy_m5stickv/sign-psbt-signed-qr-125.png b/en/img/maixpy_m5stickv/sign-psbt-signed-qr-125.png deleted file mode 100644 index 2f9b372f9..000000000 Binary files a/en/img/maixpy_m5stickv/sign-psbt-signed-qr-125.png and /dev/null differ diff --git a/en/img/maixpy_m5stickv/standard-qr-code-125.png b/en/img/maixpy_m5stickv/standard-qr-code-125.png deleted file mode 100644 index 72de2476c..000000000 Binary files a/en/img/maixpy_m5stickv/standard-qr-code-125.png and /dev/null differ diff --git a/en/img/maixpy_m5stickv/theme-1-125.png b/en/img/maixpy_m5stickv/theme-1-125.png deleted file mode 100644 index a0abdb240..000000000 Binary files a/en/img/maixpy_m5stickv/theme-1-125.png and /dev/null differ diff --git a/en/img/maixpy_m5stickv/theme-2-125.png b/en/img/maixpy_m5stickv/theme-2-125.png deleted file mode 100644 index 528a67ec1..000000000 Binary files a/en/img/maixpy_m5stickv/theme-2-125.png and /dev/null differ diff --git a/en/img/maixpy_m5stickv/theme-3-125.png b/en/img/maixpy_m5stickv/theme-3-125.png deleted file mode 100644 index 487ccba10..000000000 Binary files a/en/img/maixpy_m5stickv/theme-3-125.png and /dev/null differ diff --git a/en/img/maixpy_m5stickv/theme-4-125.png b/en/img/maixpy_m5stickv/theme-4-125.png deleted file mode 100644 index af967c3f4..000000000 Binary files a/en/img/maixpy_m5stickv/theme-4-125.png and /dev/null differ diff --git a/en/img/maixpy_m5stickv/theme-5-125.png b/en/img/maixpy_m5stickv/theme-5-125.png deleted file mode 100644 index 828b8a438..000000000 Binary files a/en/img/maixpy_m5stickv/theme-5-125.png and /dev/null differ diff --git a/en/img/maixpy_m5stickv/tools-options-125.png b/en/img/maixpy_m5stickv/tools-options-125.png deleted file mode 100644 index a0e9e2b11..000000000 Binary files a/en/img/maixpy_m5stickv/tools-options-125.png and /dev/null differ diff --git a/en/img/maixpy_m5stickv/touchscreen-125.png b/en/img/maixpy_m5stickv/touchscreen-125.png deleted file mode 100644 index 5650a5a4b..000000000 Binary files a/en/img/maixpy_m5stickv/touchscreen-125.png and /dev/null differ diff --git a/en/img/maixpy_m5stickv/wallet-load-prompt-125.png b/en/img/maixpy_m5stickv/wallet-load-prompt-125.png deleted file mode 100644 index e9bc63316..000000000 Binary files a/en/img/maixpy_m5stickv/wallet-load-prompt-125.png and /dev/null differ diff --git a/en/img/maixpy_m5stickv/wallet-type-options-125.png b/en/img/maixpy_m5stickv/wallet-type-options-125.png deleted file mode 100644 index 02874ffa5..000000000 Binary files a/en/img/maixpy_m5stickv/wallet-type-options-125.png and /dev/null differ diff --git a/en/img/maixpy_m5stickv/wallet-type-options-multisig-125.png b/en/img/maixpy_m5stickv/wallet-type-options-multisig-125.png deleted file mode 100644 index 377e720a0..000000000 Binary files a/en/img/maixpy_m5stickv/wallet-type-options-multisig-125.png and /dev/null differ diff --git a/en/img/maixpy_m5stickv/wallet-wpkh-load-prompt-125.png b/en/img/maixpy_m5stickv/wallet-wpkh-load-prompt-125.png deleted file mode 100644 index f65383c5b..000000000 Binary files a/en/img/maixpy_m5stickv/wallet-wpkh-load-prompt-125.png and /dev/null differ diff --git a/en/img/maixpy_m5stickv/wallet-wsh-load-prompt-fingerprints-125.png b/en/img/maixpy_m5stickv/wallet-wsh-load-prompt-fingerprints-125.png deleted file mode 100644 index e76b10fc0..000000000 Binary files a/en/img/maixpy_m5stickv/wallet-wsh-load-prompt-fingerprints-125.png and /dev/null differ diff --git a/en/img/maixpy_m5stickv/wallet-wsh-load-prompt-xpubs-125.png b/en/img/maixpy_m5stickv/wallet-wsh-load-prompt-xpubs-125.png deleted file mode 100644 index 142bae607..000000000 Binary files a/en/img/maixpy_m5stickv/wallet-wsh-load-prompt-xpubs-125.png and /dev/null differ diff --git a/en/img/maixpy_m5stickv/wipe-device-125.png b/en/img/maixpy_m5stickv/wipe-device-125.png deleted file mode 100644 index 19abac65c..000000000 Binary files a/en/img/maixpy_m5stickv/wipe-device-125.png and /dev/null differ diff --git a/en/img/maixpy_m5stickv/zoomed-qr-code-125.png b/en/img/maixpy_m5stickv/zoomed-qr-code-125.png deleted file mode 100644 index d783e1341..000000000 Binary files a/en/img/maixpy_m5stickv/zoomed-qr-code-125.png and /dev/null differ diff --git a/en/img/maixpy_yahboom/logo-156.png b/en/img/maixpy_yahboom/logo-156.png deleted file mode 100644 index 1cbc6070e..000000000 Binary files a/en/img/maixpy_yahboom/logo-156.png and /dev/null differ diff --git a/en/img/printing-qr.mp4 b/en/img/printing-qr.mp4 deleted file mode 100644 index 9a4ef7550..000000000 Binary files a/en/img/printing-qr.mp4 and /dev/null differ diff --git a/en/img/scanning-printed-qr.mp4 b/en/img/scanning-printed-qr.mp4 deleted file mode 100644 index 08e693955..000000000 Binary files a/en/img/scanning-printed-qr.mp4 and /dev/null differ diff --git a/en/img/sparrow/airgapped-hardware-wallet-150.png b/en/img/sparrow/airgapped-hardware-wallet-150.png deleted file mode 100644 index 96ed207e0..000000000 Binary files a/en/img/sparrow/airgapped-hardware-wallet-150.png and /dev/null differ diff --git a/en/img/sparrow/broadcast-transaction-600.png b/en/img/sparrow/broadcast-transaction-600.png deleted file mode 100644 index 123878d18..000000000 Binary files a/en/img/sparrow/broadcast-transaction-600.png and /dev/null differ diff --git a/en/img/sparrow/create-transaction-screen-600.png b/en/img/sparrow/create-transaction-screen-600.png deleted file mode 100644 index 1e7543573..000000000 Binary files a/en/img/sparrow/create-transaction-screen-600.png and /dev/null differ diff --git a/en/img/sparrow/descriptor-multisig-key-settings-600.png b/en/img/sparrow/descriptor-multisig-key-settings-600.png deleted file mode 100644 index 168e0e1a6..000000000 Binary files a/en/img/sparrow/descriptor-multisig-key-settings-600.png and /dev/null differ diff --git a/en/img/sparrow/descriptor-singlesig-key-settings-600.png b/en/img/sparrow/descriptor-singlesig-key-settings-600.png deleted file mode 100644 index 1c5330f7a..000000000 Binary files a/en/img/sparrow/descriptor-singlesig-key-settings-600.png and /dev/null differ diff --git a/en/img/sparrow/finalize-transaction-600.png b/en/img/sparrow/finalize-transaction-600.png deleted file mode 100644 index aef493095..000000000 Binary files a/en/img/sparrow/finalize-transaction-600.png and /dev/null differ diff --git a/en/img/sparrow/keystore-multisig-key-settings-600.png b/en/img/sparrow/keystore-multisig-key-settings-600.png deleted file mode 100644 index e300348a1..000000000 Binary files a/en/img/sparrow/keystore-multisig-key-settings-600.png and /dev/null differ diff --git a/en/img/sparrow/keystore-singlesig-key-settings-600.png b/en/img/sparrow/keystore-singlesig-key-settings-600.png deleted file mode 100644 index 4bf36e836..000000000 Binary files a/en/img/sparrow/keystore-singlesig-key-settings-600.png and /dev/null differ diff --git a/en/img/sparrow/krux-scan-qr-400.png b/en/img/sparrow/krux-scan-qr-400.png deleted file mode 100644 index 8d54b6211..000000000 Binary files a/en/img/sparrow/krux-scan-qr-400.png and /dev/null differ diff --git a/en/img/sparrow/multisig-receive-address-600.png b/en/img/sparrow/multisig-receive-address-600.png deleted file mode 100644 index a0f70aa6e..000000000 Binary files a/en/img/sparrow/multisig-receive-address-600.png and /dev/null differ diff --git a/en/img/sparrow/multisig-wallet-settings-600.png b/en/img/sparrow/multisig-wallet-settings-600.png deleted file mode 100644 index f6de9f9e3..000000000 Binary files a/en/img/sparrow/multisig-wallet-settings-600.png and /dev/null differ diff --git a/en/img/sparrow/new-multisig-wallet-400.png b/en/img/sparrow/new-multisig-wallet-400.png deleted file mode 100644 index 092e121e3..000000000 Binary files a/en/img/sparrow/new-multisig-wallet-400.png and /dev/null differ diff --git a/en/img/sparrow/new-singlesig-wallet-400.png b/en/img/sparrow/new-singlesig-wallet-400.png deleted file mode 100644 index 00df7cd70..000000000 Binary files a/en/img/sparrow/new-singlesig-wallet-400.png and /dev/null differ diff --git a/en/img/sparrow/show-qr-300.png b/en/img/sparrow/show-qr-300.png deleted file mode 100644 index e13e6385c..000000000 Binary files a/en/img/sparrow/show-qr-300.png and /dev/null differ diff --git a/en/img/sparrow/singlesig-receive-address-600.png b/en/img/sparrow/singlesig-receive-address-600.png deleted file mode 100644 index c37d8da33..000000000 Binary files a/en/img/sparrow/singlesig-receive-address-600.png and /dev/null differ diff --git a/en/img/sparrow/singlesig-wallet-settings-400.png b/en/img/sparrow/singlesig-wallet-settings-400.png deleted file mode 100644 index 8c00b61f4..000000000 Binary files a/en/img/sparrow/singlesig-wallet-settings-400.png and /dev/null differ diff --git a/en/img/specter/add-new-device-button-400.png b/en/img/specter/add-new-device-button-400.png deleted file mode 100644 index c4597bbcc..000000000 Binary files a/en/img/specter/add-new-device-button-400.png and /dev/null differ diff --git a/en/img/specter/add-new-wallet-button-400.png b/en/img/specter/add-new-wallet-button-400.png deleted file mode 100644 index 8e4b0f210..000000000 Binary files a/en/img/specter/add-new-wallet-button-400.png and /dev/null differ diff --git a/en/img/specter/broadcast-transaction-screen-400.png b/en/img/specter/broadcast-transaction-screen-400.png deleted file mode 100644 index 42c5fe334..000000000 Binary files a/en/img/specter/broadcast-transaction-screen-400.png and /dev/null differ diff --git a/en/img/specter/create-multisig-wallet-screen-600.png b/en/img/specter/create-multisig-wallet-screen-600.png deleted file mode 100644 index 791d1201d..000000000 Binary files a/en/img/specter/create-multisig-wallet-screen-600.png and /dev/null differ diff --git a/en/img/specter/create-singlesig-wallet-screen-600.png b/en/img/specter/create-singlesig-wallet-screen-600.png deleted file mode 100644 index fe22621a6..000000000 Binary files a/en/img/specter/create-singlesig-wallet-screen-600.png and /dev/null differ diff --git a/en/img/specter/create-transaction-screen-600.png b/en/img/specter/create-transaction-screen-600.png deleted file mode 100644 index fd6bc6cc8..000000000 Binary files a/en/img/specter/create-transaction-screen-600.png and /dev/null differ diff --git a/en/img/specter/export-wallet-screen-600.png b/en/img/specter/export-wallet-screen-600.png deleted file mode 100644 index f18fe6838..000000000 Binary files a/en/img/specter/export-wallet-screen-600.png and /dev/null differ diff --git a/en/img/specter/multisig-demo-device-400.png b/en/img/specter/multisig-demo-device-400.png deleted file mode 100644 index 620250ff9..000000000 Binary files a/en/img/specter/multisig-demo-device-400.png and /dev/null differ diff --git a/en/img/specter/multisig-segwit-key-600.png b/en/img/specter/multisig-segwit-key-600.png deleted file mode 100644 index 70c7c84b4..000000000 Binary files a/en/img/specter/multisig-segwit-key-600.png and /dev/null differ diff --git a/en/img/specter/pick-device-screen-multisig-400.png b/en/img/specter/pick-device-screen-multisig-400.png deleted file mode 100644 index e212cb35f..000000000 Binary files a/en/img/specter/pick-device-screen-multisig-400.png and /dev/null differ diff --git a/en/img/specter/pick-device-screen-singlesig-400.png b/en/img/specter/pick-device-screen-singlesig-400.png deleted file mode 100644 index 90d750647..000000000 Binary files a/en/img/specter/pick-device-screen-singlesig-400.png and /dev/null differ diff --git a/en/img/specter/receive-address-screen-400.png b/en/img/specter/receive-address-screen-400.png deleted file mode 100644 index 7f0c23f62..000000000 Binary files a/en/img/specter/receive-address-screen-400.png and /dev/null differ diff --git a/en/img/specter/scan-signed-transaction-button-400.png b/en/img/specter/scan-signed-transaction-button-400.png deleted file mode 100644 index e8fed3ecc..000000000 Binary files a/en/img/specter/scan-signed-transaction-button-400.png and /dev/null differ diff --git a/en/img/specter/select-device-type-screen-600.png b/en/img/specter/select-device-type-screen-600.png deleted file mode 100644 index 5d868faa8..000000000 Binary files a/en/img/specter/select-device-type-screen-600.png and /dev/null differ diff --git a/en/img/specter/select-wallet-type-screen-multisig-400.png b/en/img/specter/select-wallet-type-screen-multisig-400.png deleted file mode 100644 index 91e379005..000000000 Binary files a/en/img/specter/select-wallet-type-screen-multisig-400.png and /dev/null differ diff --git a/en/img/specter/select-wallet-type-screen-singlesig-400.png b/en/img/specter/select-wallet-type-screen-singlesig-400.png deleted file mode 100644 index 8c4c1e434..000000000 Binary files a/en/img/specter/select-wallet-type-screen-singlesig-400.png and /dev/null differ diff --git a/en/img/specter/single-sig-demo-device-400.png b/en/img/specter/single-sig-demo-device-400.png deleted file mode 100644 index 0932e020b..000000000 Binary files a/en/img/specter/single-sig-demo-device-400.png and /dev/null differ diff --git a/en/img/specter/single-sig-segwit-key-600.png b/en/img/specter/single-sig-segwit-key-600.png deleted file mode 100644 index 05f7ecc21..000000000 Binary files a/en/img/specter/single-sig-segwit-key-600.png and /dev/null differ diff --git a/en/img/specter/upload-keys-screen-600.png b/en/img/specter/upload-keys-screen-600.png deleted file mode 100644 index 8e8871155..000000000 Binary files a/en/img/specter/upload-keys-screen-600.png and /dev/null differ diff --git a/en/img/translation-lorem-ipsum.png b/en/img/translation-lorem-ipsum.png deleted file mode 100644 index f8d2a4e38..000000000 Binary files a/en/img/translation-lorem-ipsum.png and /dev/null differ diff --git a/en/index.html b/en/index.html deleted file mode 100644 index aa49800b4..000000000 --- a/en/index.html +++ /dev/null @@ -1,1182 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - Home - Krux - Open-source signing device firmware for Bitcoin - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - Skip to content - - -
-
- -
- - - - - - -
- - - - - - - -
- -
- - - - -
-
- - - - - - - - - - -
-
- - - - - - - -

Krux

-

- - -

-

Krux is an open-source firmware that transforms off-the-shelf Kendryte K210 devices, such as the Maix Amigo, M5StickV and more, into versatile Bitcoin transaction signers. Beyond its core functionality, Krux is a flexible platform that can adapt to devices with different form factors, providing a suite of tools to assist with the creation and recovery of mnemonic backups, some of which include encryption options for enhanced security.

-

Devices like Maix Amigo comes ready to use, with large touchscreens that make it easy and user-friendly to operate. These devices are ideal for those looking for a plug-and-play solution. On the other hand, Krux also supports development board kits, which are perfect for DIY enthusiasts who enjoy customizing and building their own hardware setups.

-

Interacting seamlessly with leading coordinator wallets through QR codes, SD cards, and even thermal printers, the firmware is continuously evolving to become a Swiss Army knife for self-custody, offering an array of features to support transactions and backups in a user-friendly and airgapped environment.

-

To learn more about Krux, check out Getting Started.

- - - - - - - - - - - - - - - - - - -
-
- - - -
- - - -
- - - -
-
-
-
- - - - - - - - - - \ No newline at end of file diff --git a/en/parts/index.html b/en/parts/index.html deleted file mode 100644 index 0eb26eb79..000000000 --- a/en/parts/index.html +++ /dev/null @@ -1,1474 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - Devices and Part List - Krux - Open-source signing device firmware for Bitcoin - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - Skip to content - - -
-
- -
- - - - - - -
- - - - - - - -
- -
- - - - -
-
- - - -
-
-
- - - - - - - - - -
-
-
- - - - -
-
- - - - - - - -

Devices and Part List

- -

Krux Compatible Devices

-

-

Comparative Table

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
DeviceM5stickVMaix AmigoMaix DockMaix BitYahboom k210 module
Price avg.US$50US$55US$35US$35US$60
Screen size / resolution1.14" / 135*2403.5" / 320*4802.4" / 240*3202.4" / 240*3202" / 240*320
Touchscreen❌Capacitive❌❌Capacitive
CameraOV7740OV7740 rear
GC0328 front
GC0328OV2640 or
OV5642
OV2640
Battery200mAh520mAh❌❌❌
RequirementsNoneNoneRotary encoder
3D printed case
Soldering
Assembly
Buttons
3D printed case
Soldering
Assembly
None
WarningsCamera has
lens distortion
Micro USB
-

: -M5stickV's USB-C port lacks pull up resistors required for it to be recognized and powered by host (computer) USB-C ports. If you don't have an USB-A available, you can use a USB hub connected between your computer's USB-C and M5stickV. -

-

: -Some Amigo screens have inverted X coordinates, others display colors differently. For more info see FAQ -

-

: -Some stores ship the Maix Dock with soldered pin connectors that do not fit into the 3D printed case -

-

All devices feature Kendryte K210 chip: -28nm process, dual-core RISC-V 64bit @400MHz, 8 MB high-speed SRAM, DVP camera and MCU LCD interface, AES Accelerator, SHA256 Accelerator, FFT Accelerator. -

-

M5StickV

-

-

Below is a list of some distributors where you can find this device:

- -
- -

Maix Amigo

-

-

Below is a list of some distributors where you can find this device:

- -
- -

Yahboom k210 module

-

-

Below is a list of some distributors where you can find this device:

- -
- -

Maix Dock and Maix Bit

-

-

For the DIYers, the Maix Dock and Maix Bit are also supported but will require sourcing the parts individually and building the device yourself.

-

Below are example implementations with instructions on how to recreate them:

- -

Below is a list of some distributors where you can find these devices:

- -
- -

Other Parts

-

USB-C Charge Cable

-

This will be included with the M5StickV and Maix Amigo that you purchase from one of the distributors above. It will be necessary to power and charge the device and to initially flash the firmware.

-

(Optional) MicroSD Card

-

We cannot guarantee that a microSD card is compatible and will work in your device; you'll need to test it on the device to be sure, read the FAQ for more info. -The size of the SD card isn't important; anything over a few megabytes will be plenty.

-

(Optional) Thermal Printer

-

Krux has the capability to print all QR codes it generates, including those for mnemonics, xpubs, wallet backups, and signed PSBTs, using a locally-connected thermal printer via its serial port.

-

Many thermal printers may be compatible, but currently, the Goojprt QR203 (easily found on AliExpress) has the best support. The Adafruit printer starter pack can also be a convenient option to get started, as it includes all the necessary components for printing (except the conversion cable). To ensure proper functionality, enable the printer driver in the Krux settings, set the Tx pin and baud rate value to either 19200 or 9600, as explained in this Adafruit printer tutorial. You will need to connect the device's Tx to the printer's Rx and ground. The printer requires a dedicated power supply, typically with an output of 5 to 9V and capable of supplying at least 2A. For more information, see this discussion.

-

(Optional) Conversion Cable for Thermal Printer

-

To connect the printer to the device, you will need a conversion cablewith a 4-pin female Grove connector on one end (to connect to the device) and 4-pin male jumpers on the other end (to connect to the printer). For a more reliable connection, it is recommended to cut and solder the wires of your custom cables instead of using jumpers.

- - - - - - - - - - - - - -
-
- - - -
- - - -
- - - -
-
-
-
- - - - - - - - - - \ No newline at end of file diff --git a/en/snippets/12th-24th-word-generate/index.html b/en/snippets/12th-24th-word-generate/index.html deleted file mode 100644 index 632a7ec85..000000000 --- a/en/snippets/12th-24th-word-generate/index.html +++ /dev/null @@ -1,1126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - 12th 24th word generate - Krux - Open-source signing device firmware for Bitcoin - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
- -
- - - - - - -
- - - - - - - -
- -
- - - - -
-
- - - -
-
-
- - - - - - - - - -
-
-
- - - - -
-
- - - - - - - -

12th 24th word generate

- -

On your 12th or 24th word, you can leave it blank to have Krux generate the final checksum word of your mnemonic for you. This is useful when you choose a manual method to generate your mnemonic and want the final BIP-39 checksum word to be valid.

- - - - - - - - - - - - - -
-
- - - -
- - - -
- - - -
-
-
-
- - - - - - - - - - \ No newline at end of file diff --git a/en/snippets/amigo-more-info-faq/index.html b/en/snippets/amigo-more-info-faq/index.html deleted file mode 100644 index 741ce7a37..000000000 --- a/en/snippets/amigo-more-info-faq/index.html +++ /dev/null @@ -1,1132 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - Amigo more info faq - Krux - Open-source signing device firmware for Bitcoin - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - Skip to content - - -
-
- -
- - - - - - -
- - - - - - - -
- -
- - - - -
-
- - - -
-
-
- - - - - - - - - -
-
-
- - - - -
-
- - - - - - - -

Amigo more info faq

- -
A note about the Maix Amigo
-

Some Amigo screens have inverted X coordinates, others display colors differently. For more info see FAQ

- - - - - - - - - - - - - -
-
- - - -
- - - -
- - - -
-
-
-
- - - - - - - - - - \ No newline at end of file diff --git a/en/snippets/flash-krux-logo/index.html b/en/snippets/flash-krux-logo/index.html deleted file mode 100644 index 14044ecbb..000000000 --- a/en/snippets/flash-krux-logo/index.html +++ /dev/null @@ -1,1130 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - Flash krux logo - Krux - Open-source signing device firmware for Bitcoin - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
- -
- - - - - - -
- - - - - - - -
- -
- - - - -
-
- - - -
-
-
- - - - - - - - - -
-
-
- - - - -
-
- - - - - - - -

Flash krux logo

- -

When the flashing process completes, you should see the Krux logo:

-

-

-

If it doesn't, try turning your device off and on by holding down the power button for six seconds.

-

Congrats, you're now running Krux!

- - - - - - - - - - - - - -
-
- - - -
- - - -
- - - -
-
-
-
- - - - - - - - - - \ No newline at end of file diff --git a/en/snippets/m5stickv-usb-c/index.html b/en/snippets/m5stickv-usb-c/index.html deleted file mode 100644 index b09ea05b7..000000000 --- a/en/snippets/m5stickv-usb-c/index.html +++ /dev/null @@ -1,1126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - M5stickv usb c - Krux - Open-source signing device firmware for Bitcoin - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
- -
- - - - - - -
- - - - - - - -
- -
- - - - -
-
- - - -
-
-
- - - - - - - - - -
-
-
- - - - -
-
- - - - - - - -

M5stickv usb c

- -

M5stickV's USB-C port lacks pull up resistors required for it to be recognized and powered by host (computer) USB-C ports. If you don't have an USB-A available, you can use a USB hub connected between your computer's USB-C and M5stickV.

- - - - - - - - - - - - - -
-
- - - -
- - - -
- - - -
-
-
-
- - - - - - - - - - \ No newline at end of file diff --git a/en/snippets/sd-card-info-faq/index.html b/en/snippets/sd-card-info-faq/index.html deleted file mode 100644 index 0c2a59bcb..000000000 --- a/en/snippets/sd-card-info-faq/index.html +++ /dev/null @@ -1,1126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - Sd card info faq - Krux - Open-source signing device firmware for Bitcoin - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
- -
- - - - - - -
- - - - - - - -
- -
- - - - -
-
- - - -
-
-
- - - - - - - - - -
-
-
- - - - -
-
- - - - - - - -

Sd card info faq

- -

We cannot guarantee that a microSD card is compatible and will work in your device; you'll need to test it on the device to be sure, read the FAQ for more info.

- - - - - - - - - - - - - -
-
- - - -
- - - -
- - - -
-
-
-
- - - - - - - - - - \ No newline at end of file diff --git a/en/snippets/single-multi-wallet-descriptor/index.html b/en/snippets/single-multi-wallet-descriptor/index.html deleted file mode 100644 index 4b9c10a65..000000000 --- a/en/snippets/single-multi-wallet-descriptor/index.html +++ /dev/null @@ -1,1129 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - Single multi wallet descriptor - Krux - Open-source signing device firmware for Bitcoin - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
- -
- - - - - - -
- - - - - - - -
- -
- - - - -
-
- - - -
-
-
- - - - - - - - - -
-
-
- - - - -
-
- - - - - - - -

Single multi wallet descriptor

- -

In Krux, select the Wallet Descriptor menu item option and scan the QR code.

-

-

-

If it worked, Krux should display the wallet information that it loaded:

- - - - - - - - - - - - - -
-
- - - -
- - - -
- - - -
-
-
-
- - - - - - - - - - \ No newline at end of file diff --git a/en/snippets/sparrow/broadcast-sparrow/index.html b/en/snippets/sparrow/broadcast-sparrow/index.html deleted file mode 100644 index a2ff6d682..000000000 --- a/en/snippets/sparrow/broadcast-sparrow/index.html +++ /dev/null @@ -1,1128 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - Broadcast sparrow - Krux - Open-source signing device firmware for Bitcoin - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
- -
- - - - - - -
- - - - - - - -
- -
- - - - -
-
- - - -
-
-
- - - - - - - - - -
-
-
- - - - -
-
- - - - - - - -

Broadcast sparrow

- -

Click the blue Broadcast Transaction button and your transaction should be broadcasted to the network!

-

-

Congratulations, you have learned how to send a transaction using Sparrow 🎉

- - - - - - - - - - - - - -
-
- - - -
- - - -
- - - -
-
-
-
- - - - - - - - - - \ No newline at end of file diff --git a/en/snippets/sparrow/import-xpub-sparrow/index.html b/en/snippets/sparrow/import-xpub-sparrow/index.html deleted file mode 100644 index a76fe3ca4..000000000 --- a/en/snippets/sparrow/import-xpub-sparrow/index.html +++ /dev/null @@ -1,1132 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - Import xpub sparrow - Krux - Open-source signing device firmware for Bitcoin - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
- -
- - - - - - -
- - - - - - - -
- -
- - - - -
-
- - - -
-
-
- - - - - - - - - -
-
-
- - - - -
-
- - - - - - - -

Import xpub sparrow

- -

Now, you will need to import your public key. To do so, press the Airgapped Hardware Wallet button under Keystores section. On the screen that pops up, look for Krux option and click its 📷 Scan... button.

-

-

-

On your Krux device, navigate to the Extended Public Key option in the main menu and click on XPUB - QR Code and show it to Sparrow.

-

-

-

It should import the xpub and show a key tab under Keystores section:

- - - - - - - - - - - - - -
-
- - - -
- - - -
- - - -
-
-
-
- - - - - - - - - - \ No newline at end of file diff --git a/en/snippets/sparrow/load-wallet-output-sparrow/index.html b/en/snippets/sparrow/load-wallet-output-sparrow/index.html deleted file mode 100644 index 403560937..000000000 --- a/en/snippets/sparrow/load-wallet-output-sparrow/index.html +++ /dev/null @@ -1,1129 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - Load wallet output sparrow - Krux - Open-source signing device firmware for Bitcoin - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
- -
- - - - - - -
- - - - - - - -
- -
- - - - -
-
- - - -
-
-
- - - - - - - - - -
-
-
- - - - -
-
- - - - - - - -

Load wallet output sparrow

- -

In Krux, select the Wallet menu item option and scan the QR code.

-

-

-

If it worked, Krux should display the wallet information that it loaded:

- - - - - - - - - - - - - -
-
- - - -
- - - -
- - - -
-
-
-
- - - - - - - - - - \ No newline at end of file diff --git a/en/snippets/sparrow/send-coins-sparrow/index.html b/en/snippets/sparrow/send-coins-sparrow/index.html deleted file mode 100644 index 1489c7dc4..000000000 --- a/en/snippets/sparrow/send-coins-sparrow/index.html +++ /dev/null @@ -1,1144 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - Send coins sparrow - Krux - Open-source signing device firmware for Bitcoin - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - Skip to content - - -
-
- -
- - - - - - -
- - - - - - - -
- -
- - - - -
-
- - - -
-
-
- - - - - - - - - -
-
-
- - - - -
-
- - - - - - - -

Send coins sparrow

- -

Send coins

-

Go to the Send screen, fill in the recipient address, amount, and any extra information you wish to supply, and click the blue Create Transaction button.

-

-

On the next screen, make sure that the Signing Wallet is the one you created and that the Sighash is set to All. Click the blue Finalize Transaction for Signing button.

-

-

On the next screen, click Show QR to make Sparrow display an animated QR code of the PSBT that you can scan with Krux by going to Sign > PSBT > Load from camera in its main menu.

-

-

After scanning, Krux should display info about the transaction for you to confirm before signing.

-

-

-

Once you have confirmed, Krux will begin animating a QR code of the signed transaction that you can scan into Sparrow.

-

-

-

In Sparrow, click Scan QR and show it the QR. A progress bar will indicate how many parts of the QR have been read.

- - - - - - - - - - - - - -
-
- - - -
- - - -
- - - -
-
-
-
- - - - - - - - - - \ No newline at end of file diff --git a/en/snippets/tips-after-install/index.html b/en/snippets/tips-after-install/index.html deleted file mode 100644 index 2a2ba98de..000000000 --- a/en/snippets/tips-after-install/index.html +++ /dev/null @@ -1,1134 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - Tips after install - Krux - Open-source signing device firmware for Bitcoin - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - Skip to content - - -
-
- -
- - - - - - -
- - - - - - - -
- -
- - - - -
-
- - - -
-
-
- - - - - - - - - -
-
-
- - - - -
-
- - - - - - - -

Tips after install

- -

Multilingual support

-

Prefer a different language? Krux has support for multiple languages. Once at the start screen, go to Settings, followed by Locale, and select the locale you wish to use.

-

Upgrade via microSD card

-

Once you've installed the initial firmware on your device via USB, you can either continue updating the device by flashing or you can perform upgrades via microSD card to keep the device airgapped.

- - - - - - - - - - - - - -
-
- - - -
- - - -
- - - -
-
-
-
- - - - - - - - - - \ No newline at end of file diff --git a/faq/index.html b/faq/index.html index 4c3d1fd54..509753ebc 100644 --- a/faq/index.html +++ b/faq/index.html @@ -16,11 +16,11 @@ - + - + @@ -28,7 +28,7 @@ - + @@ -52,6 +52,8 @@ + + @@ -76,7 +78,7 @@
- + Skip to content @@ -148,7 +150,7 @@
-
+
Initializing search @@ -165,7 +167,7 @@
- +
GitHub @@ -228,7 +230,7 @@ - Devices and Part List + Devices and Parts List @@ -258,6 +260,40 @@ +
  • + + + + + + Troubleshooting + + +
  • + + + + + + + +
  • + + + + + + Uncommon Questions + + +
  • + + + + + + +
  • @@ -313,7 +349,7 @@
    - +
    GitHub @@ -693,11 +729,11 @@
  • - + - Using a Single-sig Wallet + Setting a Coordinator and Signing @@ -714,11 +750,11 @@
  • - + - Using a Multisig Wallet + Video Tutorials @@ -752,6 +788,8 @@ + + @@ -895,6 +933,27 @@ + + + + + + +
  • + + + + + Empirical Entropy Measurement + + + + +
  • + + + + @@ -963,7 +1022,7 @@ - Devices and Part List + Devices and Parts List @@ -1021,159 +1080,109 @@ + -
  • - - - Why won't my Linux OS list a serial port after connecting my device? - - + -
  • + -
  • - - - My device didn't reboot after flashing the firmware and when I turned it off and on again, it just stayed blank without showing anything on the screen. What should I do? - - +
  • - + + -
  • - - - What are all the features available? What are the additional features of the Test or Beta version? Is there an Android app? - - - -
  • -
  • - - - Why does Krux show an xpub for a segwit address? - - -
  • - -
  • - - - Why isn't Krux scanning my QR code? - - -
  • - -
  • - - - Why am I getting an error when I try to scan a QR code? - - -
  • - -
  • - - - Why can't my computer read the QR code that Krux displays? - - -
  • - -
  • - - - Why isn't Krux detecting my microSD card or presenting an error? - - +
  • + + -
  • - -
  • - - - Why insert an SD card into my device? What is it for? Does it save something? - - + + Troubleshooting + -
  • - - + + + - + + + + + + + +
  • + + + + + Uncommon Questions + + + +
  • @@ -1218,90 +1227,21 @@

    FAQ

    -

    Why are the buttons on my Maix Amigo in the wrong order? Why is my Amigo screen displaying the wrong colors?

    -

    Some Amigo screens have inverted X coordinates while others don’t. If you notice that the buttons on keypad input screens appear to be in the wrong order, please go to Settings > Hardware > Display and change the value of Flipped X Coordinates which should correct the issue.

    -

    Others have found that there are issues with the colors displayed in the interface and camera preview. To fix this we have two options in Settings > Hardware > Display, BGR Colors and Inverted Colors, test with them until the colors appear to be correct on your device.

    -

    Why doesn't my Maix Amigo touchscreen work with v24.03.0 if it worked fine with v23.09.1?

    -

    -

    We added IRQ to the firmware, so when you open your Maix Amigo, you will see a switch in the middle of the device board, it must be in the upper position for the touchscreen to work with v24.03.0 and later.

    -
    - -

    Why isn't my device charging or being recognized when connected to the computer's USB?

    -

    If you have a Maix Amigo, make sure you're using the USB-C port at the bottom of the device, not the one on the left side.

    -

    Different computer hosts have varying hardware, operating systems, and behaviors regarding their USB ports. Below are the expected behaviors:

    -

    USB-A:

    -

    Your device should charge and turn on when connected to a USB-A port, even if it was initially turned off. You can also turn off the device while it continues to charge. However, some hosts' USB-A ports may behave like USB-C ports, as described below.

    -

    USB-C:

    -
      -
    • -

      If the device is turned off and connected to a USB-C port, it should turn on and start charging. You can turn it off again, and it will continue to charge.

      -
    • -
    • -

      If the device is already turned on and connected to a USB-C port, it may not charge or be recognized by the computer. In this case, turn off the device to initiate recognition and charging. Once turned off and reconnected, the device should restart, be recognized by the computer, and charging should be triggered by USB-C hosts. -If your device is not charging or being recognized as expected, try using a different USB port or a different computer to determine if the issue is with the device or the host's USB port.

      -
    • -
    -

    Why isn't my M5stickV device being recognized and charged when connected to the computer's USB-C?

    -

    M5stickV's USB-C port lacks pull up resistors required for it to be recognized and powered by host (computer) USB-C ports. If you don't have an USB-A available, you can use a USB hub connected between your computer's USB-C and M5stickV.

    -

    Why does my Krux device randomly freeze or restart when connected to the computer?

    -

    Windows is known to have issues with the USB-C devices. If you are experiencing random crashes or even reboots and your device does not have a battery, try using a phone charger or other power source such as a power bank.

    -

    Why won't my Linux OS list a serial port after connecting my device?

    -

    If you get the following error when trying to flash your device: Failed to find device via USB. Is it connected and powered on? -Make sure your device is being detected and serial ports are being mounted by running: -

    ls /dev/ttyUSB*
    -
    -Expect one port to be listed for devices like M5stickV and Maix Dock /dev/ttyUSB0, and two ports for Maix Amigo and Maix Bit /dev/ttyUSB0 /dev/ttyUSB1.

    -

    If you don't see them, your OS may not be loading the correct drivers to create the serial ports to connect to. Ubuntu has a known bug where the brltty driver "kidnaps" serial devices. You can solve this problem by removing it: -

    sudo apt-get remove brltty
    -

    -

    My device didn't reboot after flashing the firmware and when I turned it off and on again, it just stayed blank without showing anything on the screen. What should I do?

    -

    Check if the downloaded file matches the device, this can also occur due to data corruption. Try downloading binaries again. You can install MaixPy IDE to help with debugging, Tools > Open Terminal > New Terminal > Connect to serial port > Select a COM port available (if it doesn't work, try another COM port). It will show the terminal and some messages, a message about an empty device or with corrupted firmware appears like: "interesting, something's wrong, boot failed with exit code 233, go to find your vendor."

    -

    What are all the features available? What are the additional features of the Test or Beta version? Is there an Android app?

    -

    For official releases you will find all the features detailed here on the Getting Started page with a brief summary on the Navigation Overview page. The latest and most experimental features, which we sometimes share on our social media, can be found only in the test (beta) repository. Only official releases are signed, Test or Beta is just for trying new things and providing feedback. Krux Android app is available as an apk on the test (beta) repository (requires Android 6.0 or above).

    -

    Why does Krux show an xpub for a segwit address?

    -

    The xpub that Krux displays follows the bitcoin core descriptors spec and includes key origin and derivation info that, in theory, makes zpubs (and ypubs) unnecessary if the wallet software being shown this extra information can parse it.

    -

    From the spec:

    -
    -

    Every public key can be prefixed by an 8-character hexadecimal fingerprint plus optional derivation steps (hardened and unhardened) surrounded by brackets, identifying the master and derivation path the key or xpub that follows was derived with.

    -
    -

    However, in practice not all wallet software supports this extended format, so Krux still provides a zpub as a fallback.

    -

    For more information, check out https://outputdescriptors.org/.

    -

    Why isn't Krux scanning my QR code?

    -

    The level of detail that you see is what Krux sees. If the QR code shown on the device's screen is blurry, the camera lens of the device may be out of focus. It can be adjusted by rotating it clockwise or counter-clockwise to achieve a clearer result. The lenses usually comes with a drop of glue that makes id harder to adjust for the first time. You can use your fingertip, tweezers or small precision pliers to help, being careful to don't damage the fragile lenses.

    -

    If you have adjusted the lens already, the device may be too far away or too close to the code to read it. Start by holding the device as close to the QR code as possible and pulling away slowly until all or most of the QR code is viewable within the screen. If the code on the screen looks crisp, Krux should read it quickly and give you immediate feedback.

    -

    If you are in a dark environment, you can hold down the ENTER button of the M5StickV or Maix Amigo to turn on their LED light to potentially increase visibility. M5stickV and Amigo also has an anti-glare mode to better capture images from high brightness screens or with incident light, to enable/disable the anti-glare just press the PAGE button while scanning.

    -

    Why am I getting an error when I try to scan a QR code?

    -

    If Krux is recognizing that it sees a QR code but is displaying an error message after reading it, the likely reason is that the QR code is not in a format that Krux works with. We have listed the supported formats below:

    -

    For BIP-39 mnemonics:

    -
      -
    1. BIP-39 Plaintext (Used by Krux and https://iancoleman.io/bip39/)
    2. -
    3. SeedSigner SeedQR and CompactSeedQR Formats
    4. -
    5. UR Type crypto-bip39
    6. -
    7. Encrypted QR Code (Format created by Krux, more info here)
    8. -
    -

    For Wallet output descriptor:

    -
      -
    1. JSON with at least a descriptor key containing an output descriptor string
    2. -
    3. Key-value INI files with at least Format, Policy, and Derivation keys
    4. -
    5. UR Type crypto-output
    6. -
    -

    For PSBT (Partially Signed Bitcoin Transactions):

    -
      -
    1. Base43, Base58, and Base64-encoded bytes
    2. -
    3. Raw Bytes
    4. -
    5. UR Type crypto-psbt
    6. -
    -

    Additionally, Krux recognizes animated QR codes that use either the plaintext pMofN (the Specter QR format) or binary UR encodings.

    -

    Why can't my computer read the QR code that Krux displays?

    -

    If you are using an M5StickV, the small screen makes it difficult for laptop webcams to capture enough detail to parse the QR codes it displays. -You can toggle brightness of QR codes from public keys and PSBTs by pressing PAGE button. -In the future, more work will be done to support displaying lower density QR codes. For now, a workaround you can do is to take a picture or video of the QR code with a better-quality camera (such as your phone), then enlarge and display the photo or video to your webcam. Alternatively, it may be simpler to use a mobile wallet such as BlueWallet with the M5StickV since phone cameras don't seem to have issues reading the small QR codes. You can also save the PSBT on a microSD card for Krux to sign and then save the signed transaction to the microSD card to transfer the file to the computer or phone.

    -

    Why isn't Krux detecting my microSD card or presenting an error?

    -

    Starting from version 23.09.0, Krux supports SD card hot plugging. If you are using older versions, it may only detect the SD card at boot, so make sure Krux is turned off when inserting the microSD into it. To test the card compatibility use Krux Tools>Check SD Card. -Make sure the SD card is using MBR/DOS partition table and FAT32 format.

    -

    Here is some supported microSD cards, and here is the MaixPy FAQ explaining Why my micro SD card cannot be read.

    -

    Why insert an SD card into my device? What is it for? Does it save something?

    -

    SD card use is optional, most people use Krux only with QR codes. But you can use SD card to to upgrade the firmware, save settings, cnc/file, QR codes, XPUBs, encrypted mnemonics, and to save and load PSBTs, messages and wallet output descriptors.

    +

    Is Krux a hardware wallet?

    +

    The term "hardware wallet" typically refers to devices dedicated to storing private keys and signing transactions. These devices often feature specific security components like secure element chips.

    +

    Krux was initially developed as a signer, operating exclusively in amnesic mode, which requires users to load their keys each time the device is powered on. However, Krux has evolved and now offers the option to store mnemonics, similar to traditional hardware wallets. These mnemonics can be stored in the device's internal memory or on SD cards.

    +

    Krux does not include hardware secure elements. The security of stored data relies on encryption.

    +

    Note: Due to the inherent fragility of electronic components, never use your Krux device or SD card encrypted storage as your sole backup method. Always maintain a physical backup for added security.

    +

    What is Beta version?

    +

    The Beta version includes the latest and most experimental features, which we occasionally share on our social media. These can be found exclusively in the test (beta) repository. Use and flash the beta firmware if you are curious about new features or want to participate in the development process by hunting bugs, providing feedback, and sharing ideas in our Telegram groups or other social media platforms.

    +

    For regular use, flash the official releases, which are signed, thoroughly tested, and well-documented.

    +

    What is Krux Android app?

    +

    How can I find it?

    +

    The Krux Android app is available as an APK in the test (beta) repository. It requires Android 6.0 or above.

    +

    How can I install it?

    +

    The APK is not available on the Play Store. You can download the APK directly or transfer it to your Android device via SD card or USB cable. To install it, you may need to configure your Android device to allow installations from unknown sources.

    +

    Is it safe to use?

    +

    The Krux Android app is designed for learning about Krux and Bitcoin air-gapped transactions. Due to the numerous potential vulnerabilities inherent in smartphones, such as the lack of control over the operating system, libraries, and hardware peripherals, the Krux app should NOT be used to manage wallets containing savings or important keys and mnemonics. For secure management of your keys, a dedicated device is recommended.

    @@ -1337,7 +1277,7 @@

    - +

    - + @@ -1437,7 +1377,13 @@

    {"base": "..", "features": ["navigation.tabs", "navigation.tabs.sticky", "navigation.sections", "navigation.top", "navigation.indexes", "navigation.expand", "navigation.footer", "toc.integrate"], "search": "../assets/javascripts/workers/search.b8dbb3d2.min.js", "translations": {"clipboard.copied": "Copied to clipboard", "clipboard.copy": "Copy to clipboard", "search.result.more.one": "1 more on this page", "search.result.more.other": "# more on this page", "search.result.none": "No matching documents", "search.result.one": "1 matching document", "search.result.other": "# matching documents", "search.result.placeholder": "Type to start searching", "search.result.term.missing": "Missing", "select.version": "Select version"}} - + + + + + + + diff --git a/getting-started/features/QR-transcript-tools/index.html b/getting-started/features/QR-transcript-tools/index.html index eba7dfd8a..69c0b387d 100644 --- a/getting-started/features/QR-transcript-tools/index.html +++ b/getting-started/features/QR-transcript-tools/index.html @@ -16,11 +16,11 @@ - + - + @@ -28,7 +28,7 @@ - + @@ -52,6 +52,8 @@ + + @@ -148,7 +150,7 @@
    -
    +
    Initializing search @@ -165,7 +167,7 @@
    - +
    GitHub @@ -230,7 +232,7 @@ - Devices and Part List + Devices and Parts List @@ -258,6 +260,40 @@ +
  • + + + + + + Troubleshooting + + +
  • + + + + + + + +
  • + + + + + + Uncommon Questions + + +
  • + + + + + + +
  • @@ -313,7 +349,7 @@
    - +
    GitHub @@ -702,11 +738,11 @@
  • - + - Using a Single-sig Wallet + Setting a Coordinator and Signing @@ -723,11 +759,11 @@
  • - + - Using a Multisig Wallet + Video Tutorials @@ -763,6 +799,8 @@ + + @@ -988,6 +1026,27 @@ + + + + + + +
  • + + + + + Empirical Entropy Measurement + + + + +
  • + + + + @@ -1056,7 +1115,7 @@ - Devices and Part List + Devices and Parts List @@ -1091,6 +1150,46 @@ +
  • + + + + + Troubleshooting + + + + +
  • + + + + + + + + + +
  • + + + + + Uncommon Questions + + + + +
  • + + + + + + + + +
  • @@ -1125,7 +1224,7 @@

    Transcribing QR Codes

    -

    When you export a mnemonic, encrypted mnemonic or a generic text QR code, alternative visualization modes will be available. Swipe left or right to change modes, or if your device doesn't have a touchscreen, press the Page buttons. Find transcribe templates here.

    +

    When you export a mnemonic, encrypted mnemonic or a generic text QR code, alternative visualization modes will be available. Swipe left or right to change modes, or if your device doesn't have a touchscreen, press the PAGE buttons. Find transcribe templates here.

    Standard Mode

    @@ -1153,7 +1252,7 @@

    Highlighted Regions ModeGrided Mode

    -

    Grids will be added to a standard QR code. In a dark room, if you place a sheet of paper over the device's screen, you'll notice QR code will be visible and it will be possible to copy it directly from above. Be careful to don't damage your screen with pen and markers, use an insulating plastic tape or film to protect the device if you use this method.

    +

    Grids will be added to a standard QR code. In a dark room, if you place a sheet of paper over the device's screen, you'll notice QR code will be visible and it will be possible to copy it directly from above (tracing). Be careful not to damage your screen with pen and markers, use an insulating plastic tape or film to protect the device when using this method.

    @@ -1207,13 +1306,13 @@

    Grided Mode + @@ -1290,7 +1389,13 @@

    Grided Mode{"base": "../../..", "features": ["navigation.tabs", "navigation.tabs.sticky", "navigation.sections", "navigation.top", "navigation.indexes", "navigation.expand", "navigation.footer", "toc.integrate"], "search": "../../../assets/javascripts/workers/search.b8dbb3d2.min.js", "translations": {"clipboard.copied": "Copied to clipboard", "clipboard.copy": "Copy to clipboard", "search.result.more.one": "1 more on this page", "search.result.more.other": "# more on this page", "search.result.none": "No matching documents", "search.result.one": "1 matching document", "search.result.other": "# matching documents", "search.result.placeholder": "Type to start searching", "search.result.term.missing": "Missing", "select.version": "Select version"}} - + + + + + + + diff --git a/getting-started/features/encrypted-mnemonics/index.html b/getting-started/features/encrypted-mnemonics/index.html index 786920a84..1e356750d 100644 --- a/getting-started/features/encrypted-mnemonics/index.html +++ b/getting-started/features/encrypted-mnemonics/index.html @@ -13,14 +13,14 @@ - + - + @@ -28,7 +28,7 @@ - + @@ -52,6 +52,8 @@ + + @@ -148,7 +150,7 @@

  • @@ -258,6 +260,40 @@ +
  • + + + + + + Troubleshooting + + +
  • + + + + + + + +
  • + + + + + + Uncommon Questions + + +
  • + + + + + + +
  • @@ -313,7 +349,7 @@
    - +
    GitHub @@ -702,11 +738,11 @@
  • - + - Using a Single-sig Wallet + Setting a Coordinator and Signing @@ -723,11 +759,11 @@
  • - + - Using a Multisig Wallet + Video Tutorials @@ -763,6 +799,8 @@ + + @@ -1018,6 +1056,27 @@ + + + + + + +
  • + + + + + Empirical Entropy Measurement + + + + +
  • + + + + @@ -1086,7 +1145,7 @@ - Devices and Part List + Devices and Parts List @@ -1121,6 +1180,46 @@ +
  • + + + + + Troubleshooting + + + + +
  • + + + + + + + + + +
  • + + + + + Uncommon Questions + + + + +
  • + + + + + + + + +
  • @@ -1213,7 +1312,7 @@

    Encrypted QR Codes Data and Parsing

    Considerations

    Storage of encrypted mnemonics on the device or SD cards are meant for convenience only and should not be considered a form of backup. Always make a physical backup of your keys that is independent from electronic devices and test recovering your wallet from this backup before you send funds to it.

    -

    Remember that the stored encrypted mnemonic is protected by the key you defined to encrypt it. If the defined key is weak, your encrypted mnemonic will not be protected. If you have stored a mnemonic with funds in the device's internal flash memory using a weak key, the best way to undo this is to wipe the device.

    +

    Remember that the stored encrypted mnemonic is protected by the key you defined to encrypt it. If the defined key is weak, your encrypted mnemonic will not be protected. If you have stored a mnemonic with funds in the device's internal flash memory using a weak key, the best way to undo this is to wipe the device.

    @@ -1249,7 +1348,7 @@

    Considerations - +

  • @@ -1307,7 +1406,7 @@

    Considerations - + @@ -1315,15 +1414,15 @@

    Considerations - + - - + + @@ -1331,7 +1430,7 @@

    Considerations - +

    @@ -1349,7 +1448,13 @@

    Considerations{"base": "../../..", "features": ["navigation.tabs", "navigation.tabs.sticky", "navigation.sections", "navigation.top", "navigation.indexes", "navigation.expand", "navigation.footer", "toc.integrate"], "search": "../../../assets/javascripts/workers/search.b8dbb3d2.min.js", "translations": {"clipboard.copied": "Copied to clipboard", "clipboard.copy": "Copy to clipboard", "search.result.more.one": "1 more on this page", "search.result.more.other": "# more on this page", "search.result.none": "No matching documents", "search.result.one": "1 matching document", "search.result.other": "# matching documents", "search.result.placeholder": "Type to start searching", "search.result.term.missing": "Missing", "select.version": "Select version"}} - + + + + + + + diff --git a/getting-started/usage/using-a-single-sig-wallet/index.html b/getting-started/features/entropy/index.html similarity index 53% rename from getting-started/usage/using-a-single-sig-wallet/index.html rename to getting-started/features/entropy/index.html index 11fa2321c..c471a6fed 100644 --- a/getting-started/usage/using-a-single-sig-wallet/index.html +++ b/getting-started/features/entropy/index.html @@ -10,25 +10,25 @@ - + - + - + - + - Using a Single-sig Wallet - Krux - Open-source signing device firmware for Bitcoin + Empirical Entropy Measurement - Krux - Open-source signing device firmware for Bitcoin - + @@ -52,6 +52,8 @@ + + @@ -76,7 +78,7 @@
    - + Skip to content @@ -111,7 +113,7 @@
    - Using a Single-sig Wallet + Empirical Entropy Measurement
    @@ -148,7 +150,7 @@
    -
    +
    Initializing search @@ -165,7 +167,7 @@
    - +
    GitHub @@ -230,7 +232,7 @@ - Devices and Part List + Devices and Parts List @@ -258,6 +260,40 @@ +
  • + + + + + + Troubleshooting + + +
  • + + + + + + + +
  • + + + + + + Uncommon Questions + + +
  • + + + + + + +
  • @@ -313,7 +349,7 @@
    - +
    GitHub @@ -579,8 +615,6 @@ - - @@ -607,11 +641,13 @@ -
  • +
  • - + + + -
  • @@ -1206,7 +1310,13 @@

    Printing

    - + + + + + + + diff --git a/getting-started/features/sd-card-update/index.html b/getting-started/features/sd-card-update/index.html index 4e3311d33..39bfa67f6 100644 --- a/getting-started/features/sd-card-update/index.html +++ b/getting-started/features/sd-card-update/index.html @@ -20,7 +20,7 @@ - + @@ -28,7 +28,7 @@ - + @@ -52,6 +52,8 @@ + + @@ -148,7 +150,7 @@
    -
    +
    Initializing search @@ -165,7 +167,7 @@
    - +
    @@ -1230,7 +1329,13 @@

    Upgrade via microSD card{"base": "../../..", "features": ["navigation.tabs", "navigation.tabs.sticky", "navigation.sections", "navigation.top", "navigation.indexes", "navigation.expand", "navigation.footer", "toc.integrate"], "search": "../../../assets/javascripts/workers/search.b8dbb3d2.min.js", "translations": {"clipboard.copied": "Copied to clipboard", "clipboard.copy": "Copy to clipboard", "search.result.more.one": "1 more on this page", "search.result.more.other": "# more on this page", "search.result.none": "No matching documents", "search.result.one": "1 matching document", "search.result.other": "# matching documents", "search.result.placeholder": "Type to start searching", "search.result.term.missing": "Missing", "select.version": "Select version"}} - + + + + + + + diff --git a/getting-started/features/tools/index.html b/getting-started/features/tools/index.html index 5024ecbd0..dad82ce1d 100644 --- a/getting-started/features/tools/index.html +++ b/getting-started/features/tools/index.html @@ -20,7 +20,7 @@ - + @@ -28,7 +28,7 @@ - + @@ -52,6 +52,8 @@ + + @@ -148,7 +150,7 @@
    -
    +
    Initializing search @@ -165,7 +167,7 @@
    - +
    GitHub @@ -230,7 +232,7 @@ - Devices and Part List + Devices and Parts List @@ -258,6 +260,40 @@ +
  • + + + + + + Troubleshooting + + +
  • + + + + + + + +
  • + + + + + + Uncommon Questions + + +
  • + + + + + + +
  • @@ -313,7 +349,7 @@
    - +
    GitHub @@ -702,11 +738,11 @@
  • - + - Using a Single-sig Wallet + Setting a Coordinator and Signing @@ -723,11 +759,11 @@
  • - + - Using a Multisig Wallet + Video Tutorials @@ -763,6 +799,8 @@ + + @@ -938,6 +976,15 @@ +
  • + +
  • + + + Descriptor Addresses + + +
  • @@ -988,6 +1035,27 @@ + + + + + + +
  • + + + + + Empirical Entropy Measurement + + + + +
  • + + + + @@ -1056,7 +1124,7 @@ - Devices and Part List + Devices and Parts List @@ -1091,6 +1159,46 @@ +
  • + + + + + Troubleshooting + + + + +
  • + + + + + + + + + +
  • + + + + + Uncommon Questions + + + + +
  • + + + + + + + + +
  • @@ -1131,7 +1239,7 @@

    Tools

    Check SD Card

    -

    You can check if a SD card can be detected and read by your device and explore its content.

    +

    You can check if a SD card can be detected and read by your device and explore its content. If there are too many files to fit on one screen, swipe up or down to navigate between the screens if your device has a touchscreen.

    @@ -1143,9 +1251,11 @@

    -

    Enter a text input to create, print or transcript a QR code that can be later used as an encryption key or as a passphrase. Swipe left or right to change modes if your device has a touchscreen.

    +

    Enter text to create, print or transcribe a QR code that can later be used as an encryption key or passphrase. Swipe left or right to change modes if your device has a touchscreen.

    +

    Descriptor Addresses

    +

    Verify if an address or list of addresses belong to a wallet without needing to load private keys. Simply load a trusted wallet descriptor from a QR code or SD card.

    Remove Mnemonic

    @@ -1252,7 +1362,7 @@

    Wipe Device - + @@ -1260,15 +1370,15 @@

    Wipe Device - + - - + + @@ -1276,7 +1386,7 @@

    Wipe Device - +

  • @@ -1294,7 +1404,13 @@

    Wipe Device{"base": "../../..", "features": ["navigation.tabs", "navigation.tabs.sticky", "navigation.sections", "navigation.top", "navigation.indexes", "navigation.expand", "navigation.footer", "toc.integrate"], "search": "../../../assets/javascripts/workers/search.b8dbb3d2.min.js", "translations": {"clipboard.copied": "Copied to clipboard", "clipboard.copy": "Copy to clipboard", "search.result.more.one": "1 more on this page", "search.result.more.other": "# more on this page", "search.result.none": "No matching documents", "search.result.one": "1 matching document", "search.result.other": "# matching documents", "search.result.placeholder": "Type to start searching", "search.result.term.missing": "Missing", "select.version": "Select version"}} - + + + + + + + diff --git a/getting-started/index.html b/getting-started/index.html index c9083ddba..ff7d0d8bf 100644 --- a/getting-started/index.html +++ b/getting-started/index.html @@ -20,7 +20,7 @@ - + @@ -28,7 +28,7 @@ - + @@ -52,6 +52,8 @@ + + @@ -143,7 +145,7 @@
    -
    +
    Initializing search @@ -160,7 +162,7 @@
    - +
    GitHub @@ -225,7 +227,7 @@ - Devices and Part List + Devices and Parts List @@ -253,6 +255,40 @@ +
  • + + + + + + Troubleshooting + + +
  • + + + + + + + +
  • + + + + + + Uncommon Questions + + +
  • + + + + + + +
  • @@ -308,7 +344,7 @@
    - +
    GitHub @@ -697,11 +733,11 @@
  • - + - Using a Single-sig Wallet + Setting a Coordinator and Signing @@ -718,11 +754,11 @@
  • - + - Using a Multisig Wallet + Video Tutorials @@ -756,6 +792,8 @@ + + @@ -902,6 +940,27 @@ + + + + + + +
  • + + + + + Empirical Entropy Measurement + + + + +
  • + + + + @@ -970,7 +1029,7 @@ - Devices and Part List + Devices and Parts List @@ -1005,6 +1064,46 @@ +
  • + + + + + Troubleshooting + + + + +
  • + + + + + + + + + +
  • + + + + + Uncommon Questions + + + + +
  • + + + + + + + + +
  • @@ -1040,14 +1139,14 @@

    Getting Started

    -

    Krux is open-source Bitcoin signing firmware for devices with the K210 chipset.

    -

    Signing operations in Krux are done offline via QR code or via SD card. You can create/load your BIP-39 mnemonic, or import a wallet output descriptor, and sign transactions all without having to plug the device into your computer (except to initially install the firmware). It reads QR codes with its camera and outputs QR codes to its screen, or to paper via an optional thermal printer attachment.

    -

    Krux does not come with its own desktop wallet software. Instead, you can use Krux with third-party wallet coordinators to create/manage wallets, and send transactions from your online computer or mobile device while keeping your keys offline. Krux was built to be vendor agnostic and works with many popular wallet coordinators, including:

    +

    Krux is open-source Bitcoin signing firmware for devices with the K210 chipset; also known as a hardware signer.

    +

    Signing operations in Krux are done offline via QR code or via SD card using the PSBT functionality. You can create/load your BIP-39 mnemonic, or import a wallet descriptor, and sign transactions all without having to plug the device into your computer (except to initially install the firmware). It reads QR codes with its camera and outputs QR codes to its screen, or to paper via an optional thermal printer attachment.

    +

    Krux runs offline, and therefore never handles the broadcasting part of the PSBT transaction. Instead, you can use Krux with third-party wallet coordinators to broadcast transactions from your online computer or mobile device while keeping your keys offline. Krux was built to be vendor agnostic and works with many popular wallet coordinators, including:

    @@ -1142,7 +1241,7 @@

    Getting Started

    - + @@ -1150,15 +1249,15 @@

    Getting Started

    - + - - + + @@ -1166,7 +1265,7 @@

    Getting Started

    - +
  • @@ -1184,7 +1283,13 @@

    Getting Started

    - + + + + + + + diff --git a/getting-started/installing/from-gui/index.html b/getting-started/installing/from-gui/index.html index 6da341254..0aee929c2 100644 --- a/getting-started/installing/from-gui/index.html +++ b/getting-started/installing/from-gui/index.html @@ -20,7 +20,7 @@ - + @@ -28,7 +28,7 @@ - + @@ -52,6 +52,8 @@ + + @@ -148,7 +150,7 @@
    -
    +
    Initializing search @@ -165,7 +167,7 @@
    - +
    @@ -1599,7 +1698,13 @@

    Upgrade via microSD card{"base": "../../..", "features": ["navigation.tabs", "navigation.tabs.sticky", "navigation.sections", "navigation.top", "navigation.indexes", "navigation.expand", "navigation.footer", "toc.integrate"], "search": "../../../assets/javascripts/workers/search.b8dbb3d2.min.js", "translations": {"clipboard.copied": "Copied to clipboard", "clipboard.copy": "Copy to clipboard", "search.result.more.one": "1 more on this page", "search.result.more.other": "# more on this page", "search.result.none": "No matching documents", "search.result.one": "1 matching document", "search.result.other": "# matching documents", "search.result.placeholder": "Type to start searching", "search.result.term.missing": "Missing", "select.version": "Select version"}} - + + + + + + + diff --git a/getting-started/installing/from-pre-built-release/index.html b/getting-started/installing/from-pre-built-release/index.html index ae1aeba70..0cb3342b9 100644 --- a/getting-started/installing/from-pre-built-release/index.html +++ b/getting-started/installing/from-pre-built-release/index.html @@ -20,7 +20,7 @@ - + @@ -28,7 +28,7 @@ - + @@ -52,6 +52,8 @@ + + @@ -148,7 +150,7 @@
    -
    +
    Initializing search @@ -165,7 +167,7 @@
    - +
    GitHub @@ -230,7 +232,7 @@ - Devices and Part List + Devices and Parts List @@ -258,6 +260,40 @@ +
  • + + + + + + Troubleshooting + + +
  • + + + + + + + +
  • + + + + + + Uncommon Questions + + +
  • + + + + + + +
  • @@ -313,7 +349,7 @@
    - +
  • @@ -807,11 +876,11 @@
  • - + - Using a Single-sig Wallet + Setting a Coordinator and Signing @@ -828,11 +897,11 @@
  • - + - Using a Multisig Wallet + Video Tutorials @@ -866,6 +935,8 @@ + + @@ -1012,6 +1083,27 @@ + + + + + + +
  • + + + + + Empirical Entropy Measurement + + + + +
  • + + + + @@ -1080,7 +1172,7 @@ - Devices and Part List + Devices and Parts List @@ -1115,6 +1207,46 @@ +
  • + + + + + Troubleshooting + + + + +
  • + + + + + + + + + +
  • + + + + + Uncommon Questions + + + + +
  • + + + + + + + + +
  • @@ -1155,22 +1287,25 @@

    Download the latest releaseVerify the files

    Before installing the release, it's a good idea to check that:

      -
    1. The SHA256 hash of krux-vX.Y.Z.zip matches the hash in krux-vX.Y.Z.zip.sha256.txt
    2. -
    3. The signature file krux-vX.Y.Z.zip.sig can be verified with the selfcustody.pem public key found in the root of the krux repository.
    4. +
    5. The SHA256 hash of krux-vX.Y.Z.zip matches the hash in krux-vX.Y.Z.zip.sha256.txt
    6. +
    7. The signature file krux-vX.Y.Z.zip.sig can be verified with the selfcustody.pem public key found in the root of the krux repository.

    You can either do this manually or with the krux shell script, which contains helper commands for this:

    ./krux sha256 krux-vX.Y.Z.zip
     ./krux verify krux-vX.Y.Z.zip selfcustody.pem
     

    +

    On Mac you may need to install coreutils to be able to use sha256sum +

    brew install coreutils
    +

    Fun fact: Each Krux release is signed with Krux!

    Flash the firmware onto the device

    Extract the latest version of Krux you downloaded and enter the folder:

    unzip krux-vX.Y.Z.zip && cd krux-vX.Y.Z
     

    -

    Connect the device to your computer via USB (for Maix Amigo, make sure you’re using bottom port), power it on, and run the following, replacing DEVICE with either m5stickv, amigo, bit or yahboom (to yahboom you may need to manually specify the port): +

    Connect the device to your computer via USB (for Maix Amigo, make sure you’re using bottom port), power it on, and run the following, replacing DEVICE with either m5stickv, amigo, bit, cube, dock or yahboom (to yahboom you may need to manually specify the port, for example /dev/ttyUSB0 on Linux or COM6 on Windows):

    ./ktool -B goE -b 1500000 maixpy_DEVICE/kboot.kfpkg
     

    -

    For dock the -B parameter changes, so run: +

    For dock use the -B dan parameter:

    ./ktool -B dan -b 1500000 maixpy_dock/kboot.kfpkg
     

    When the flashing process completes, you should see the Krux logo:

    @@ -1179,20 +1314,27 @@

    Flash the firmware onto the deviceIf it doesn't, try turning your device off and on by holding down the power button for six seconds.

    Congrats, you're now running Krux!

    A note about the Maix Amigo
    -

    Some Amigo screens have inverted X coordinates, others display colors differently. For more info see FAQ

    +

    Some Amigo screens have inverted X coordinates, others display colors differently. For more info see Troubleshooting.

    Troubleshooting

    If ktool fails to run, you may need to give it executable permissions with chmod +x ./ktool, or you might need to use "sudo" if your user don't have access to serial port. In Windows or Mac you may need to explicitly allow the tool to run by adding an exception for it.

    If the flashing process fails midway through, check the connection, restart the device, and try the command again.

    -

    Two serial ports are created when Amigo and Bit are connected to a PC. Sometimes Ktool will pick the wrong and flash will fail. Manually specify the serial port to overcome this issue using -p argument:

    +

    Two serial ports are created when Amigo and Bit are connected to a PC. Sometimes Ktool will pick the wrong port and flashing will fail. Manually specify the serial port to overcome this issue using -p argument:

    +
    Linux
    +

    See the correct port using ls /dev/ttyUSB*, in the example below we use /dev/ttyUSB0:

    ./ktool-linux -B goE -b 1500000 maixpy_amigo/kboot.kfpkg -p /dev/ttyUSB1
    -
    -

    Check por names of devices manager on Windows (e.g. COM1, COM9), or list the ports on linux

    -
    ls /dev/ttyUSB*
    -
    -

    List ports on Mac

    -

    ls /dev/cu.usbserial*
    -
    -Different OS versions may have different port names, and the absence of ports may indicate a connection, driver or hardware related issue.

    +
  • +
    Windows
    +

    See the correct port at Device Manager > Ports (COM & LPT), in the example below we use COM6: +

    .\ktool-win.exe -B goE -b 1500000 maixpy_amigo\kboot.kfpkg -p COM6
    +

    +
    Mac
    +

    Remove the Gatekeeper quarantine extended attribute from ktool-mac: +

    xattr -d com.apple.quarantine ktool-mac
    +

    +

    See the correct port using the command line: ls /dev/cu.usbserial*, in the example below we use /dev/cu.usbserial-10 (If the output isn't what you expect try a different cable, preferably a smartphone usb-c charger cable): +

    ./ktool-mac -B goE -b 1500000 maixpy_amigo/kboot.kfpkg -p /dev/cu.usbserial-10
    +

    +

    Different OS versions may have different port names, and the absence of ports may indicate a connection, driver or hardware related issue. See Troubleshooting for more info.

    Multilingual support

    Prefer a different language? Krux has support for multiple languages. Once at the start screen, go to Settings, followed by Locale, and select the locale you wish to use.

    Upgrade via microSD card

    @@ -1290,7 +1432,7 @@

    Upgrade via microSD card - + @@ -1298,15 +1440,15 @@

    Upgrade via microSD card - + - - + + @@ -1314,7 +1456,7 @@

    Upgrade via microSD card - +

    @@ -1332,7 +1474,13 @@

    Upgrade via microSD card{"base": "../../..", "features": ["navigation.tabs", "navigation.tabs.sticky", "navigation.sections", "navigation.top", "navigation.indexes", "navigation.expand", "navigation.footer", "toc.integrate"], "search": "../../../assets/javascripts/workers/search.b8dbb3d2.min.js", "translations": {"clipboard.copied": "Copied to clipboard", "clipboard.copy": "Copy to clipboard", "search.result.more.one": "1 more on this page", "search.result.more.other": "# more on this page", "search.result.none": "No matching documents", "search.result.one": "1 matching document", "search.result.other": "# matching documents", "search.result.placeholder": "Type to start searching", "search.result.term.missing": "Missing", "select.version": "Select version"}} - + + + + + + + diff --git a/getting-started/installing/from-source/index.html b/getting-started/installing/from-source/index.html index bcbb2e8a1..619ef9f50 100644 --- a/getting-started/installing/from-source/index.html +++ b/getting-started/installing/from-source/index.html @@ -20,7 +20,7 @@ - + @@ -28,7 +28,7 @@ - + @@ -52,6 +52,8 @@ + + @@ -76,7 +78,7 @@
    - + Skip to content @@ -148,7 +150,7 @@
    -
    +
    Initializing search @@ -165,7 +167,7 @@
    - +
    GitHub @@ -230,7 +232,7 @@ - Devices and Part List + Devices and Parts List @@ -258,6 +260,40 @@ +
  • + + + + + + Troubleshooting + + +
  • + + + + + + + +
  • + + + + + + Uncommon Questions + + +
  • + + + + + + +
  • @@ -313,7 +349,7 @@
    - +
  • + + + + + Troubleshooting + + + + +
  • + + + + + + + + + +
  • + + + + + Uncommon Questions + + + + +
  • + + + + + + + + +
  • @@ -1206,76 +1284,72 @@

    From source

    -

    This page explains how to install Krux from source. You can check these instructions in our README too.

    -

    Software

    -

    You will need a computer with git and vagrant installed.

    +

    This page explains how to install Krux from source. You can check a simplified version of these instructions in our README too.

    Fetch the code

    -

    In a terminal, run the following: +

    This will download the source code of Krux as well as the code of all its dependencies inside a new folder called krux (needs git):

    git clone --recurse-submodules https://github.com/selfcustody/krux
    -
    -This will pull down the Krux source code as well as the code for all its dependencies and put them inside a new krux folder.

    -

    Note: When you wish to pull down updates to this repo, you can run the following inside the krux folder: -

    git pull origin main && git submodule update --init --recursive
     

    -

    Spin up a virtual machine

    -

    After you have installed Vagrant, run the following inside the krux folder to spin up a new VM: -

    vagrant up
    +

    Note: When you wish to pull updates (to all submodules, their submodules, ...) to this repo, use: +

    git pull origin main && git submodule update --init --recursive
     

    -

    Build the firmware

    Prerequisite for upgrading via microSD

    -

    If you wish to perform airgapped upgrades via microSD card, you will need to have a private and public key pair to sign your builds and verify the signatures.

    +

    If you wish to perform airgapped upgrades via microSD card later, you will need to have a private and public key pair to sign your builds and verify the signatures. If you do not want to perform further airgapped upgrades, jump to build section.

    You can use an existing Krux installation and mnemonic to sign your builds with, or you can generate a keypair and sign from the openssl CLI. Commands have been added to the krux shell script to make this easier.

    In either case, you will need to update the SIGNER_PUBKEY field in src/krux/metadata.py to store your public key so that Krux can verify future builds before installing.

    -

    To generate a keypair, run: -

    vagrant ssh -c 'cd /vagrant; ./krux generate-keypair'
    -vagrant ssh -c 'cd /vagrant; ./krux pem-to-pubkey pubkey.pem'
    +

    To generate a keypair: +

    ./krux generate-keypair
    +./krux pem-to-pubkey pubkey.pem
     

    The first command will create privkey.pem and pubkey.pem files you can use with openssl, and the second command will output your public key in the form expected by Krux.

    Once you've updated the SIGNER_PUBKEY with this value, you can proceed with the regular build process.

    -

    Build

    -

    Run the following, replacing DEVICE with either m5stickv, amigo, dock, bit or yahboom: -

    vagrant ssh -c 'cd /vagrant; ./krux build maixpy_DEVICE'
    +

    Build the firmware (Linux or WSL)

    +

    The krux bash script contains commands for common development tasks. It assumes a Linux host, you will need to have Docker Desktop or Docker Engine, openssl, and wget installed at a minimum for the commands to work as expected. It works on Windows using WSL. The channel Crypto Guide from Youtube made a step-by-step video - Krux DIY Bitcoin Signer: Build From Source & Verify (With Windows + WSL2 + Docker)

    +

    To build and flash the firmware: +

    # build firmware for Maix Amigo
    +./krux build maixpy_amigo
    +

    +

    The first time, the build can take around an hour or so to complete. Subsequent builds should take only a few minutes. If all goes well, you should see a new build folder containing firmware.bin and kboot.kfpkg files when the build completes.

    +

    Note: if you encounter any of these errors while building, it is a problem connecting to github, try again (if the error persists, try changing the DNS/VPN or correcting the hostname resolution of github.com to an IP that is working for you): +

    error: RPC failed; curl 92 HTTP/2 stream 0 was not closed cleanly: CANCEL (err8)
    +fatal: the remote end hung up unexpectedly
    +fatal: early EOF
    +fatal: index-pack failed
    +fatal: clone of ... failed
    +Failed to clone ...
     

    -

    This will take around an hour or so to complete the first time. Subsequent builds should take only a few minutes.

    -

    If all goes well, you should see a new build folder containing firmware.bin and kboot.kfpkg files when the build completes.

    -

    Reproducibility

    +

    Reproducibility

    If you build from the main branch of the source code, you should be able to reproduce the build process used to generate the last release binaries and obtain exact copies of the firmware.bin and kboot.kfpkg files, with matching hash checksums.

    To extract and verify the firmware.bin contained in kboot.kfpkg, you can use the following command:

    -

    unzip kboot.kfpkg -d ./kboot/

    +
    unzip kboot.kfpkg -d ./kboot/
    +

    Flash the firmware onto the device

    -

    Connect the device to your computer via USB (for Maix Amigo, make sure you’re using bottom port), power it on, and run the following, replacing DEVICE with either m5stickv, amigo, dock, bit or yahboom: -

    vagrant ssh -c 'cd /vagrant; ./krux flash maixpy_DEVICE'
    +

    Connect the device to your computer via USB (for Maix Amigo, make sure you’re using bottom port), power it on, and run the following, replacing DEVICE with either m5stickv, amigo, bit, cube, dock or yahboom: +

    # build firmware for DEVICE
    +./krux flash maixpy_DEVICE
     
    -If the flashing fails try one of the following common solutions:

    -
      -
    • Running vagrant reload prior to flashing in order for the newly-inserted USB device to be detected and passed through to the VM on startup.
    • -
    • If this command fails, even after reloading, with the error Failed to find device via USB. Is it connected and powered on?, make sure that your user has been added to the vboxusers group. On Mac or Linux, run the following command: -
      sudo usermod -a -G vboxusers yourusername
      -
    • -
    • If the flashing process fails midway through, check the connection, restart the device, and try the command again.
    • -
    +If the flashing fails try one of the following common solutions listed on FAQ

    When the flashing process completes, you should see the Krux logo:

    If it doesn't, try turning your device off and on by holding down the power button for six seconds.

    Congrats, you're now running Krux!

    A note about the Maix Amigo
    -

    Some Amigo screens have inverted X coordinates, others display colors differently. For more info see FAQ

    +

    Some Amigo screens have inverted X coordinates, others display colors differently. For more info see Troubleshooting.

    Signing the firmware

    -

    You can sign the firmware using one of the two methods listed below:

    +

    You can sign the firmware to perform airgapped upgrades using one of the two methods listed below:

    Method 1: Signing from Krux

    First, calculate the SHA256 hash of the new firmware by running: -

    vagrant ssh -c 'cd /vagrant; ./krux sha256 build/firmware.bin'
    +
    ./krux sha256 build/firmware.bin
     

    Copy this hex string and turn it into a QR code using whichever QR code generator you'd like.

    In Krux, enter the mnemonic of your private key that will be used for signing, and go to Sign > Message. Scan the QR code you generated, and you will be asked if you wish to sign the hash. Proceed, and you will be presented with a base64-encoded string containing the signature, as text and as a QR code.

    Take this string and create a signature file by running: -

    vagrant ssh -c 'cd /vagrant; ./krux b64decode "signature-in-base64" > build/firmware.bin.sig'
    +
    ./krux b64decode "signature-in-base64" > build/firmware.bin.sig
     

    This will generate a firmware.bin.sig file containing a signature of the firmware's SHA256 hash.

    Method 2: Signing from your computer with OpenSSL

    With the keypair you generated before, you can now run: -

    vagrant ssh -c 'cd /vagrant; ./krux sign build/firmware.bin privkey.pem'
    +
    ./krux sign build/firmware.bin privkey.pem
     

    This will generate a firmware.bin.sig file containing a signature of the firmware's SHA256 hash.

    @@ -1371,7 +1445,7 @@

    Method 2: Signing from - + @@ -1379,15 +1453,15 @@

    Method 2: Signing from - + - - + + @@ -1395,7 +1469,7 @@

    Method 2: Signing from - +

    @@ -1413,7 +1487,13 @@

    Method 2: Signing from - + + + + + + + diff --git a/getting-started/installing/from-test-release/index.html b/getting-started/installing/from-test-release/index.html index e6f1da62b..6d7cab756 100644 --- a/getting-started/installing/from-test-release/index.html +++ b/getting-started/installing/from-test-release/index.html @@ -20,7 +20,7 @@ - + @@ -28,7 +28,7 @@ - + @@ -52,6 +52,8 @@ + + @@ -148,7 +150,7 @@

  • @@ -258,6 +260,40 @@ +
  • + + + + + + Troubleshooting + + +
  • + + + + + + + +
  • + + + + + + Uncommon Questions + + +
  • + + + + + + +
  • @@ -313,7 +349,7 @@
    - +
  • + +
  • + + + Compiled firmware for Kendryte K210 devices + + +
  • @@ -618,6 +663,15 @@ +
  • + +
  • + + + Mac + + +
  • @@ -651,6 +705,15 @@ +
  • + +
  • + + + Mac + + +
  • @@ -693,6 +756,15 @@ +
  • + +
  • + + + Mac + + +
  • @@ -707,6 +779,48 @@ +
  • + +
  • + + + Sipeed Maix Cube + + + + +
  • @@ -720,7 +834,7 @@ @@ -1209,7 +1373,7 @@ - Devices and Part List + Devices and Parts List @@ -1244,6 +1408,46 @@ +
  • + + + + + Troubleshooting + + + + +
  • + + + + + + + + + +
  • + + + + + Uncommon Questions + + + + +
  • + + + + + + + + +
  • @@ -1282,52 +1486,81 @@

    From pre-built test release

    Warning

    Keep in mind that these are unsigned binaries.

    Download

    -

    Download experimental compiled firmware or the Android app apk: Krux binaries

    +

    Download experimental compiled firmware or the Android app apk from our test (beta) repository.

    Android

    -

    Krux Android app is intended for learning about Krux and Bitcoin airgapped transactions. Vulnerabilities inherent to Android phones such as the OS, other apps and wireless / CDMA / GSM / Bluetooth connectivity make using any phone insecure. Krux app should NOT be used to manage savings or important keys and mnemonics. For that, a dedicated device is recommended.

    +

    The Krux Android app is designed for learning about Krux and Bitcoin air-gapped transactions. Due to the numerous potential vulnerabilities inherent in smartphones, such as the lack of control over the operating system, libraries, and hardware peripherals, the Krux app should NOT be used to manage wallets containing savings or important keys and mnemonics. For secure management of your keys, a dedicated device is recommended. More

    +

    Compiled firmware for Kendryte K210 devices

    M5StickV

    +

    To Flash M5StickV run the following.

    Linux
    -

    To Flash M5stickV run:

    ./ktool-linux -B goE -b 1500000 maixpy_m5stickv/kboot.kfpkg
    -

    +
  • +
    Mac
    +
    ./ktool-mac -B goE -b 1500000 maixpy_m5stickv/kboot.kfpkg
    +
    Windows
    -

    Replace './ktool-linux' for 'ktool-win.exe' and / for \ in commands: -

    ./ktool-win.exe -B goE -b 1500000 maixpy_m5stickv\kboot.kfpkg
    -

    +
    .\ktool-win.exe -B goE -b 1500000 maixpy_m5stickv\kboot.kfpkg
    +

    Sipeed Maix Amigo

    +

    To Flash Maix Amigo run the following.

    Linux
    -

    To Flash Maix Amigo run:

    ./ktool-linux -B goE -b 1500000 maixpy_amigo/kboot.kfpkg
    -

    +
    +
    Mac
    +
    ./ktool-mac -B goE -b 1500000 maixpy_amigo/kboot.kfpkg
    +
    Windows
    -

    Replace './ktool-linux' for 'ktool-win.exe' and / for \ in commands: -

    ./ktool-win.exe -B goE -b 1500000 maixpy_amigo\kboot.kfpkg
    -

    +
    .\ktool-win.exe -B goE -b 1500000 maixpy_amigo\kboot.kfpkg
    +
    A note about the Maix Amigo
    -

    Some Amigo screens have inverted X coordinates, others display colors differently. For more info see FAQ

    +

    Some Amigo screens have inverted X coordinates, others display colors differently. For more info see Troubleshooting.

    Sipeed Maix Bit

    +

    To Flash Maix Bit run the following.

    Linux
    -

    To Flash Maix Bit run:

    ./ktool-linux -B goE -b 1500000 maixpy_bit/kboot.kfpkg
    -

    +
    +
    Mac
    +
    ./ktool-mac -B goE -b 1500000 maixpy_bit/kboot.kfpkg
    +
    Windows
    -

    Replace './ktool-linux' for 'ktool-win.exe' and / for \ in commands: -

    ./ktool-win.exe -B goE -b 1500000 maixpy_bit\kboot.kfpkg
    -

    -

    Sipeed Maix Dock

    +
    .\ktool-win.exe -B goE -b 1500000 maixpy_bit\kboot.kfpkg
    +
    +

    Sipeed Maix Cube

    +

    To Flash Maix Cube run the following.

    Linux
    -

    To Flash Maix Dock run: -

    ./ktool-linux -B dan -b 1500000 maixpy_dock/kboot.kfpkg
    -

    +
    ./ktool-linux -B goE -b 1500000 maixpy_cube/kboot.kfpkg
    +
    +
    Mac
    +
    ./ktool-mac -B goE -b 1500000 maixpy_cube/kboot.kfpkg
    +
    Windows
    -

    Replace './ktool-linux' for 'ktool-win.exe' and / for \ in commands: -

    ./ktool-win.exe -B dan -b 1500000 maixpy_dock\kboot.kfpkg
    -

    -

    Aimotion Yahboom k210 module

    +
    .\ktool-win.exe -B goE -b 1500000 maixpy_cube\kboot.kfpkg
    +
    +

    Sipeed Maix Dock

    +

    To Flash Maix Dock you need to pass the -B dan parameter.

    Linux
    -

    To Flash Yahboom k210 module you'll have to manually specify the port, on this example /dev/ttyUSB0: +

    ./ktool-linux -B dan -b 1500000 maixpy_dock/kboot.kfpkg
    +
    +
    Mac
    +
    ./ktool-mac -B dan -b 1500000 maixpy_dock/kboot.kfpkg
    +
    +
    Windows
    +
    .\ktool-win.exe -B dan -b 1500000 maixpy_dock\kboot.kfpkg
    +
    +

    Aimotion Yahboom k210 module

    +

    To Flash Yahboom k210 module you'll have to manually specify the port.

    +
    Linux
    +

    See the correct port using ls /dev/ttyUSB*, in the example below we use /dev/ttyUSB0:

    ./ktool-linux -B goE -b 1500000 -p /dev/ttyUSB0 yahboom/kboot.kfpkg
     

    +
    Mac
    +

    See the correct port using the command line: ls /dev/cu.usbserial*, in the example below we use /dev/cu.usbserial-10: +

    ./ktool-mac -B goE -b 1500000 -p /dev/cu.usbserial-10 yahboom/kboot.kfpkg
    +

    +
    Windows
    +

    See the correct port at Device Manager > Ports (COM & LPT), in the example below we use COM6: +

    .\ktool-win.exe -B goE -b 1500000 -p COM6 yahboom\kboot.kfpkg
    +

    @@ -1421,7 +1654,7 @@
    Linux - + @@ -1429,15 +1662,15 @@
    Linux - + - - + + @@ -1445,7 +1678,7 @@
    Linux - +
    @@ -1463,7 +1696,13 @@
    Linux{"base": "../../..", "features": ["navigation.tabs", "navigation.tabs.sticky", "navigation.sections", "navigation.top", "navigation.indexes", "navigation.expand", "navigation.footer", "toc.integrate"], "search": "../../../assets/javascripts/workers/search.b8dbb3d2.min.js", "translations": {"clipboard.copied": "Copied to clipboard", "clipboard.copy": "Copy to clipboard", "search.result.more.one": "1 more on this page", "search.result.more.other": "# more on this page", "search.result.none": "No matching documents", "search.result.one": "1 matching document", "search.result.other": "# matching documents", "search.result.placeholder": "Type to start searching", "search.result.term.missing": "Missing", "select.version": "Select version"}} - + + + + + + + diff --git a/getting-started/installing/index.html b/getting-started/installing/index.html index 2cb1d9484..03c26ac7d 100644 --- a/getting-started/installing/index.html +++ b/getting-started/installing/index.html @@ -20,7 +20,7 @@ - + @@ -28,7 +28,7 @@ - + @@ -52,6 +52,8 @@ + + @@ -148,7 +150,7 @@
    -
    +
    Initializing search @@ -165,7 +167,7 @@
    - +
    GitHub @@ -230,7 +232,7 @@ - Devices and Part List + Devices and Parts List @@ -258,6 +260,40 @@ +
  • + + + + + + Troubleshooting + + +
  • + + + + + + + +
  • + + + + + + Uncommon Questions + + +
  • + + + + + + +
  • @@ -313,7 +349,7 @@
    - +
    GitHub @@ -702,11 +738,11 @@
  • - + - Using a Single-sig Wallet + Setting a Coordinator and Signing @@ -723,11 +759,11 @@
  • - + - Using a Multisig Wallet + Video Tutorials @@ -761,6 +797,8 @@ + + @@ -907,6 +945,27 @@ + + + + + + +
  • + + + + + Empirical Entropy Measurement + + + + +
  • + + + + @@ -975,7 +1034,7 @@ - Devices and Part List + Devices and Parts List @@ -1010,6 +1069,46 @@ +
  • + + + + + Troubleshooting + + + + +
  • + + + + + + + + + +
  • + + + + + Uncommon Questions + + + + +
  • + + + + + + + + +
  • @@ -1052,7 +1151,7 @@

    Installing

  • From source
  • Requirements

    -

    Please, check the part list for the compatible devices and requirements.

    +

    Please, check the parts list for the compatible devices and requirements.

    After the first firmware install, you can use a microSD card if you wish to perform further airgapped updates.

    @@ -1147,7 +1246,7 @@

    Requirements - + @@ -1155,15 +1254,15 @@

    Requirements - + - - + + @@ -1171,7 +1270,7 @@

    Requirements - +

    @@ -1189,7 +1288,13 @@

    Requirements{"base": "../..", "features": ["navigation.tabs", "navigation.tabs.sticky", "navigation.sections", "navigation.top", "navigation.indexes", "navigation.expand", "navigation.footer", "toc.integrate"], "search": "../../assets/javascripts/workers/search.b8dbb3d2.min.js", "translations": {"clipboard.copied": "Copied to clipboard", "clipboard.copy": "Copy to clipboard", "search.result.more.one": "1 more on this page", "search.result.more.other": "# more on this page", "search.result.none": "No matching documents", "search.result.one": "1 matching document", "search.result.other": "# matching documents", "search.result.placeholder": "Type to start searching", "search.result.term.missing": "Missing", "select.version": "Select version"}} - + + + + + + + diff --git a/getting-started/navigation/index.html b/getting-started/navigation/index.html index 96f1e50fb..1e2dc9322 100644 --- a/getting-started/navigation/index.html +++ b/getting-started/navigation/index.html @@ -20,7 +20,7 @@ - + @@ -28,7 +28,7 @@ - + @@ -52,6 +52,8 @@ + + @@ -148,7 +150,7 @@
    -
    +
    Initializing search @@ -165,7 +167,7 @@
    - +
    GitHub @@ -230,7 +232,7 @@ - Devices and Part List + Devices and Parts List @@ -258,6 +260,40 @@ +
  • + + + + + + Troubleshooting + + +
  • + + + + + + + +
  • + + + + + + Uncommon Questions + + +
  • + + + + + + +
  • @@ -313,7 +349,7 @@
    - +
    GitHub @@ -702,11 +738,11 @@
  • - + - Using a Single-sig Wallet + Setting a Coordinator and Signing @@ -723,11 +759,11 @@
  • - + - Using a Multisig Wallet + Video Tutorials @@ -761,6 +797,8 @@ + + @@ -907,6 +945,27 @@ + + + + + + +
  • + + + + + Empirical Entropy Measurement + + + + +
  • + + + + @@ -1029,7 +1088,7 @@ - Devices and Part List + Devices and Parts List @@ -1064,6 +1123,46 @@ +
  • + + + + + Troubleshooting + + + + +
  • + + + + + + + + + +
  • + + + + + Uncommon Questions + + + + +
  • + + + + + + + + +
  • @@ -1156,13 +1255,13 @@

    Home Menu (Loaded a mnemonic) + @@ -1239,7 +1338,13 @@

    Home Menu (Loaded a mnemonic){"base": "../..", "features": ["navigation.tabs", "navigation.tabs.sticky", "navigation.sections", "navigation.top", "navigation.indexes", "navigation.expand", "navigation.footer", "toc.integrate"], "search": "../../assets/javascripts/workers/search.b8dbb3d2.min.js", "translations": {"clipboard.copied": "Copied to clipboard", "clipboard.copy": "Copy to clipboard", "search.result.more.one": "1 more on this page", "search.result.more.other": "# more on this page", "search.result.none": "No matching documents", "search.result.one": "1 matching document", "search.result.other": "# matching documents", "search.result.placeholder": "Type to start searching", "search.result.term.missing": "Missing", "select.version": "Select version"}} - + + + + + + + diff --git a/getting-started/settings/index.html b/getting-started/settings/index.html index 10ad7464a..b295be654 100644 --- a/getting-started/settings/index.html +++ b/getting-started/settings/index.html @@ -13,14 +13,14 @@ - + - + @@ -28,7 +28,7 @@ - + @@ -52,6 +52,8 @@ + + @@ -76,7 +78,7 @@

  • @@ -258,6 +260,40 @@ +
  • + + + + + + Troubleshooting + + +
  • + + + + + + + +
  • + + + + + + Uncommon Questions + + +
  • + + + + + + +
  • @@ -313,7 +349,7 @@
    - +
    GitHub @@ -702,11 +738,11 @@
  • - + - Using a Single-sig Wallet + Setting a Coordinator and Signing @@ -723,11 +759,11 @@
  • - + - Using a Multisig Wallet + Video Tutorials @@ -761,6 +797,8 @@ + + @@ -907,6 +945,27 @@ + + + + + + +
  • + + + + + Empirical Entropy Measurement + + + + +
  • + + + + @@ -964,12 +1023,36 @@
    +

    Security

    +

    Adjust settings that may impact your security protocols.

    +

    Shutdown Time

    +

    Set the time it takes for Krux to automatically shut down. This feature not only conserves your device's battery, if it has one, but also serves as an important security measure. If you forget your device with private keys loaded, it will shut down automatically after the set time.

    +

    Please note that devices without batteries and power management will not shut down but will reboot instead, which is sufficient to unload private keys.

    +

    Hide Mnemonics

    +

    When "Hide Mnemonics" mode is set to "True", your device will not display private key data or backup tools when a key is loaded. It will only show public key information and allow signing operations.

    Appearance

    @@ -1432,7 +1601,7 @@

    Factory Settings - +

    @@ -1490,7 +1659,7 @@

    Factory Settings - + @@ -1498,15 +1667,15 @@

    Factory Settings - + - - + + @@ -1514,7 +1683,7 @@

    Factory Settings - +

    @@ -1532,7 +1701,13 @@

    Factory Settings{"base": "../..", "features": ["navigation.tabs", "navigation.tabs.sticky", "navigation.sections", "navigation.top", "navigation.indexes", "navigation.expand", "navigation.footer", "toc.integrate"], "search": "../../assets/javascripts/workers/search.b8dbb3d2.min.js", "translations": {"clipboard.copied": "Copied to clipboard", "clipboard.copy": "Copy to clipboard", "search.result.more.one": "1 more on this page", "search.result.more.other": "# more on this page", "search.result.none": "No matching documents", "search.result.one": "1 matching document", "search.result.other": "# matching documents", "search.result.placeholder": "Type to start searching", "search.result.term.missing": "Missing", "select.version": "Select version"}} - + + + + + + + diff --git a/getting-started/usage/generating-a-mnemonic/index.html b/getting-started/usage/generating-a-mnemonic/index.html index 562047dcd..aa3b5e751 100644 --- a/getting-started/usage/generating-a-mnemonic/index.html +++ b/getting-started/usage/generating-a-mnemonic/index.html @@ -20,7 +20,7 @@ - + @@ -28,7 +28,7 @@ - + @@ -52,6 +52,8 @@ + + @@ -148,7 +150,7 @@
    -
    +
    Initializing search @@ -165,7 +167,7 @@
    - +
    GitHub @@ -230,7 +232,7 @@ - Devices and Part List + Devices and Parts List @@ -258,6 +260,40 @@ +
  • + + + + + + Troubleshooting + + +
  • + + + + + + + +
  • + + + + + + Uncommon Questions + + +
  • + + + + + + +
  • @@ -313,7 +349,7 @@
    - +
    GitHub @@ -702,6 +738,15 @@ +
  • + +
  • + + + Words + + +
  • @@ -777,15 +822,6 @@ -
  • - -
  • - - - Alternatives - - -
  • @@ -846,11 +882,11 @@
  • - + - Using a Single-sig Wallet + Setting a Coordinator and Signing @@ -867,11 +903,11 @@
  • - + - Using a Multisig Wallet + Video Tutorials @@ -905,6 +941,8 @@ + + @@ -1051,6 +1089,27 @@ + + + + + + +
  • + + + + + Empirical Entropy Measurement + + + + +
  • + + + + @@ -1119,7 +1178,7 @@ - Devices and Part List + Devices and Parts List @@ -1154,6 +1213,46 @@ +
  • + + + + + Troubleshooting + + + + +
  • + + + + + + + + + +
  • + + + + + Uncommon Questions + + + + +
  • + + + + + + + + +
  • @@ -1188,8 +1287,8 @@

    Generating a Mnemonic

    -

    Krux has support for creating 12 and 24-word mnemonic seed phrases. Since true entropy is difficult to produce, especially with an embedded device, we recommend to outsource entropy generation using dice rolls, but it is also possible to use camera as a source of entropy to quickly create a mnemonic. -At the start screen, once you select New Mnemonic, you will be taken to a second menu where you can choose to create a mnemonic via camera, via rolls of a D6 (standard six-sided die) or D20 (20-sided die).

    +

    Krux supports creating 12 and 24-word BIP-39 mnemonic seed phrases. Since generating true entropy is challenging, especially with an embedded device, we recommend outsourcing entropy generation using dice rolls. However, it is also possible to randomly pick words (e.g., SeedPicker) or use the camera as a source of entropy to quickly create a mnemonic.

    +

    At the start screen, after selecting New Mnemonic, you will be taken to a second menu where you can choose to create a mnemonic via the camera, words, rolls of a D6 (standard six-sided die), or a D20 (20-sided die).

    Camera

    @@ -1200,12 +1299,15 @@

    Camera

    -

    Image Entropy Quality Estimation

    +

    Image Entropy Quality Estimation

    During image capture, entropy quality estimation is displayed to assist you in obtaining a high-quality image source for your key. After a snapshot is taken, Shannon's entropy and pixel deviation indices are presented. Minimum thresholds are established to prevent the use of poor-quality images with low entropy for key generation. It's important to note that these values serve as indicators or estimations of entropy quality, but they are not absolute entropy values in a cryptographic context.

    +

    Words

    +

    Print the BIP39 word list in 3D or on paper, then cut out the words and place them in a bucket. Manually draw 11 or 23 words from the bucket. +For the final word, Krux will assist you in picking a valid 12th or 24th word by adjusting its smart keypad to only allow typing words with a valid checksum. Alternatively, you can leave it empty, and Krux will select a final, valid checksum word for you.

    Dice Rolls

    Via D6

    Choose between 12 or 24 words.

    @@ -1229,6 +1331,7 @@

    Dice Rolls Entropy Quality Estima

    When you input your dice rolls, you'll see two progress bars filling up. The top progress bar shows how many rolls you've entered compared to the minimum number needed. The bottom progress bar shows the real-time calculated Shannon's entropy compared to the required minimum (128 bits for 12 words and 256 bits for 24 words). When the Shannon's entropy estimation reaches the recommended level, the progress bar will be full, and its frame will change color. If you've met the minimum number of rolls but the entropy estimation is still below the recommended level, a warning will appear, suggesting you add more rolls to increase entropy. Note: Similar to image entropy quality estimation, dice rolls Shannon's entropy serves as an indicator and should not be considered an absolute measure of cryptographic entropy.

    +

    Learn more about Krux Entropy Quality Estimation

    Stats for Nerds

    A low Shannon's entropy value might suggest that your dice are biased or that there's a problem with how you're gathering entropy. To investigate further, examine the "Stats for Nerds" section to check the distribution of your rolls and look for any abnormalities.

    @@ -1250,9 +1353,7 @@

    How it works

  • How to verify

    -

    Don't trust, verify. We encourage you not to trust any claim you cannot verify yourself. Therefore, there are wallets that use compatible algorithms to calculate the entropy derived from dice rolls. You can use the SeedSigner or Coldcard hardware wallets, or even the Bitcoiner Guide website, they share the same logic that Krux uses and will give the same mnemonic for the dice roll method.

    -

    Alternatives

    -

    You can use any other offline airgapped devices to generate your mnemonic. If you have an old Android smartphone that is offline (in airplane mode [no active CDMA or GSM chip], no Wifi connection, no Bluetooth and localization service turned off), you can use the Krux app for Android. If you want to use a regular PC, a common strategy is to boot the PC using Tails from a USB stick, without connecting the device to the internet, and then you can run Krux using our simulator, use a copy of the the Bitcoiner Guide website or even Ian Coleman's BIP-39 Tool. It's worth noting that both generate a QR code that Krux can read via the QR input method mentioned on the next page (Loading a Mnemonic).

    +

    Don't trust, verify. We encourage you not to trust any claim you cannot verify yourself. Therefore, there are wallets that use compatible algorithms to calculate the entropy derived from dice rolls. You can use the SeedSigner or Coldcard hardware wallets, or even the Bitcoiner Guide website, they share the same logic that Krux uses and will give the same mnemonic for the dice roll method.

    @@ -1346,7 +1447,7 @@

    Alternatives - + @@ -1354,15 +1455,15 @@

    Alternatives - + - - + + @@ -1370,7 +1471,7 @@

    Alternatives - +

    @@ -1388,7 +1489,13 @@

    Alternatives{"base": "../../..", "features": ["navigation.tabs", "navigation.tabs.sticky", "navigation.sections", "navigation.top", "navigation.indexes", "navigation.expand", "navigation.footer", "toc.integrate"], "search": "../../../assets/javascripts/workers/search.b8dbb3d2.min.js", "translations": {"clipboard.copied": "Copied to clipboard", "clipboard.copy": "Copy to clipboard", "search.result.more.one": "1 more on this page", "search.result.more.other": "# more on this page", "search.result.none": "No matching documents", "search.result.one": "1 matching document", "search.result.other": "# matching documents", "search.result.placeholder": "Type to start searching", "search.result.term.missing": "Missing", "select.version": "Select version"}} - + + + + + + + diff --git a/getting-started/usage/loading-a-mnemonic/index.html b/getting-started/usage/loading-a-mnemonic/index.html index bab3b52a5..e517fbb7d 100644 --- a/getting-started/usage/loading-a-mnemonic/index.html +++ b/getting-started/usage/loading-a-mnemonic/index.html @@ -20,7 +20,7 @@ - + @@ -28,7 +28,7 @@ - + @@ -52,6 +52,8 @@ + + @@ -148,7 +150,7 @@
    -
    +
    Initializing search @@ -165,7 +167,7 @@
    - +
    GitHub @@ -230,7 +232,7 @@ - Devices and Part List + Devices and Parts List @@ -258,6 +260,40 @@ +
  • + + + + + + Troubleshooting + + +
  • + + + + + + + +
  • + + + + + + Uncommon Questions + + +
  • + + + + + + +
  • @@ -313,7 +349,7 @@
    - +
  • - -
  • - - - Final checksum word - - -
  • @@ -843,13 +870,13 @@
  • - + - Wallet loading sequence + Wallet Loading -
  • @@ -930,11 +987,11 @@
  • - + - Using a Single-sig Wallet + Setting a Coordinator and Signing @@ -951,11 +1008,11 @@
  • - + - Using a Multisig Wallet + Video Tutorials @@ -989,6 +1046,8 @@ + + @@ -1135,6 +1194,27 @@ + + + + + + +
  • + + + + + Empirical Entropy Measurement + + + + +
  • + + + + @@ -1203,7 +1283,7 @@ - Devices and Part List + Devices and Parts List @@ -1238,6 +1318,46 @@ +
  • + + + + + Troubleshooting + + + + +
  • + + + + + + + + + +
  • + + + + + Uncommon Questions + + + + +
  • + + + + + + + + +
  • @@ -1272,7 +1392,7 @@

    Loading a Mnemonic

    -

    Once you have either a 12- or 24-word mnemonic, choose Load Mnemonic on Krux's start menu, and you will be presented with several input methods:

    +

    Once you have either a 12- or 24-word BIP-39 mnemonic, choose Load Mnemonic on Krux's start menu, and you will be presented with several input methods:

    Input Methods

    @@ -1280,16 +1400,17 @@

    Input Methods

    Via Camera

    You can choose to use the camera to scan a QR code or Tiny Seed metal plate backup.

    +

    If you are in a dark environment, you can hold down the ENTER button of the M5StickV or Maix Amigo to turn on their LED light to potentially increase visibility. Some cameras (OV7740 and OV2640) have an anti-glare mode to better capture images from high brightness screens or with incident light, they are present on M5StickV, Amigo, Cube and some Yahboom. To enable/disable the anti-glare mode on a supported device just press the PAGE button while scanning.

    QR Code

    It's unpleasant having to manually enter 12 or 24 words every time you want to use Krux. To remedy this you can instead use the device's camera to read a QR code containing the words. Krux will decode QR codes of four types:

    -
      +
      • Plain text QR: The mnemonic words encoded as text, with words separated by spaces.
      • SeedQR: Basically, it is the mnemonic words of the respective BIP-39 numbers concatenated, encoded as text.
      • Compact SeedQR: Basically, it is the mnemonic words bits concatenated as bytes.
      • Encrypted Mnemonic: A specification created by Krux that encrypts the mnemonic words bits and adds some information about the encryption used.
      • -
    +

    After opening your wallet via one of the manual methods you can use Krux to create QR codes of all types above, transcript them to paper or metal using the transcription helpers or attach a thermal printer to your Krux and print out the mnemonic. Check out the Printing section for more information. You can also use an offline QR code generator for this (ideally on an airgapped device).

    Tiny Seed

    @@ -1305,18 +1426,15 @@

    Words&

    Enter each word of your BIP-39 mnemonic one at a time. Krux will disable impossible-to-reach letters as you type and will attempt to autocomplete your words to speed up the process.

    -

    On your 12th or 24th word, you can leave it blank to have Krux generate the final checksum word of your mnemonic for you. This is useful when you choose a manual method to generate your mnemonic and want the final BIP-39 checksum word to be valid.

    Word Numbers

    Decimal
    -

    Enter each word of your BIP-39 mnemonic as a number from 1 to 2048 one at a time. You can use this list for reference.

    +

    Enter each word of your BIP-39 mnemonic as a number (1-2048) one at a time. You can use this list for reference.

    Hexadecimal and Octal

    You can also enter your BIP-39 mnemonic word's numbers (1-2048) in hexadecimal format, with values ranging from 0x1 to 0x800, or in octal format, with values ranging from 01 to 04000. This is useful with some metal plate backups that uses those formats.

    -
    Final checksum word
    -

    On your 12th or 24th word, you can leave it blank to have Krux generate the final checksum word of your mnemonic for you. This is useful when you choose a manual method to generate your mnemonic and want the final BIP-39 checksum word to be valid.

    Tiny Seed (Bits)

    @@ -1337,30 +1455,81 @@

    From StorageYou can retrieve mnemonics previously stored on device's internal flash or external (SD card). All stored mnemonics are encrypted, to load them you'll have to enter the same key you used to encrypt them.

    -

    Wallet loading sequence

    +

    Wallet Loading

    Confirm Mnemonic Words

    Once you have entered your mnemonic, you will be presented with the full list of words to confirm.

    -

    Passphrase

    -

    -

    -

    After confirming the mnemonic words, you can optionally choose to type or scan a BIP-39 passphrase. When typing, swipe left or right to change modes if your device has a touchscreen. For scanning, you can also create a QR code from your offline passphrase in Tools.

    +

    Confirm Wallet Attributes

    +

    You will be presented with a screen containing wallet attributes, if they are as expected just press Load Wallet and you'll be ready to use your loaded key.

    +

    +

    +

    Attributes

    +

    Fingerprint

    +

    + 73c5da0a +

    + +

    The BIP-32 master wallet's fingerprint, if you have it noted down, will help you make sure you entered the correct mnemonic and passphrase (optional) and will load the expected wallet.

    +

    Network

    +

    + Mainnet +

    + +

    Check if you are loading a Testnet or Mainnet wallet.

    +

    Single/Multisig

    +

    + Single-sig +

    + +

    Check if you are loading a Single-sig or Multisig wallet.

    +

    Derivation Path

    +

    + m/84'/0'/0' +

    + +

    The derivation path is a sequence of numbers, or "nodes", that define the script type, network, and account index of your wallet.

    +
      +
    1. +

      Script Type: The first number defines the script type. The default is 84', corresponding to a Native Segwit wallet. Other values include:

      +

      44' for Legacy

      +

      49' for Nested Segwit

      +

      86' for Taproot

      +

      48' for Multisig

      +
    2. +
    3. +

      Network: The second number defines the network:

      +

      0' for Mainnet

      +

      1' for Testnet

      +
    4. +
    5. +

      Account Index: The third number is the account index, with 0' being the default.

      +
    6. +
    +

    For multisig wallets, a fourth node with the value 2' is added to the derivation path.

    +

    Passphrase

    +

    + No Passphrase +

    + +

    Informs if the wallet has a loaded passphrase.

    +

    Changing Wallet Attributes

    +

    You can change any of the attributes before and after loading a wallet. +It is also possible to change default settings for Network and Single/Multisig on settings.

    +

    Passphrase

    +

    +

    +

    You can type or scan a BIP-39 passphrase. When typing, swipe left or right to change keypads if your device has a touchscreen. For scanning, you can also create a QR code from your offline passphrase in Tools.

    -

    Fingerprint

    -

    -

    -

    The wallet's fingerprint, if you have it noted down, will help you make sure you entered the correct mnemonic and passphrase (optional) and will load the expected wallet.

    +

    Customize

    +

    +

    +

    Press Customize to open a menu where you can change the Network, Single/Multisig, Script Type and Account.

    -

    Single-sig or Multisig

    -

    -

    -

    After loading your mnemonic and passphrase (optional), you will be asked if you want to use it as part of a Single-sig or Multisig wallet.

    -

    Your choice here will subtly change the generated xpub that is used to set up your device in your wallet coordinator software. You can learn more about the difference in the following guides for using single-sig and multisig wallets.

    Now, onto the main menu...

    @@ -1456,7 +1625,7 @@

    Single-sig or Multisig - + @@ -1464,15 +1633,15 @@

    Single-sig or Multisig - + - - + + @@ -1480,7 +1649,7 @@

    Single-sig or Multisig - +

  • @@ -1498,7 +1667,13 @@

    Single-sig or Multisig{"base": "../../..", "features": ["navigation.tabs", "navigation.tabs.sticky", "navigation.sections", "navigation.top", "navigation.indexes", "navigation.expand", "navigation.footer", "toc.integrate"], "search": "../../../assets/javascripts/workers/search.b8dbb3d2.min.js", "translations": {"clipboard.copied": "Copied to clipboard", "clipboard.copy": "Copy to clipboard", "search.result.more.one": "1 more on this page", "search.result.more.other": "# more on this page", "search.result.none": "No matching documents", "search.result.one": "1 matching document", "search.result.other": "# matching documents", "search.result.placeholder": "Type to start searching", "search.result.term.missing": "Missing", "select.version": "Select version"}} - + + + + + + + diff --git a/getting-started/usage/navigating-the-main-menu/index.html b/getting-started/usage/navigating-the-main-menu/index.html index c53ff7821..214542cfa 100644 --- a/getting-started/usage/navigating-the-main-menu/index.html +++ b/getting-started/usage/navigating-the-main-menu/index.html @@ -16,11 +16,11 @@ - + - + @@ -28,7 +28,7 @@ - + @@ -52,6 +52,8 @@ + + @@ -76,7 +78,7 @@
    - + Skip to content @@ -148,7 +150,7 @@
    -
    +
    Initializing search @@ -165,7 +167,7 @@
    - +
    GitHub @@ -230,7 +232,7 @@ - Devices and Part List + Devices and Parts List @@ -258,6 +260,40 @@ +
  • + + + + + + Troubleshooting + + +
  • + + + + + + + +
  • + + + + + + Uncommon Questions + + +
  • + + + + + + +
  • @@ -313,7 +349,7 @@
    - +
  • + + + + + Troubleshooting + + + + +
  • + + + + + + + + + +
  • + + + + + Uncommon Questions + + + + +
  • + + + + + + + + +
  • @@ -1322,89 +1385,159 @@

    Navigating the Main Menu

    -

    Mnemonic

    +

    Backup Mnemonic

    -

    This will open a new submenu with the following options to backup your mnemonic. If you set a printer driver, it will also give the option to print the backup!

    -
    - -

    Words

    -

    -

    -

    Display the mnemonic words as text so you can write them down.

    -
    - -

    Numbers

    -

    -

    -

    Display the mnemonic word numbers in decimal, hex, or octal format.

    +

    This will open a new submenu with different types of backups. QR Code based, Encrypted and Other Formats

    +

    If you set a printer, it will also give the option to print them!

    -

    Plaintext QR

    +

    QR Code

    +
      +
    • Plaintext QR
    • +

    -

    Generate a QR containing the mnemonic words as regular text, where words are separated by spaces.

    +

    Generate a QR containing the mnemonic words as regular text, where words are separated by spaces. Any QR code can be printed if a thermal printer driver is set.

    -

    Compact SeedQR

    +
      +
    • Compact SeedQR
    • +

    -

    A QR code is created from a binary representation of mnemonic words. Format created by SeedSigner -backup-qr-compact

    +

    A QR code is created from a binary representation of mnemonic words. Format created by SeedSigner, more info here.

    -

    SeedQR

    +
      +
    • SeedQR
    • +

    -

    Words are converted to their BIP-39 numeric indexes, those numbers are then concatenated as a string and finally converted to a QR code. Format created by SeedSigner

    +

    Words are converted to their BIP-39 numeric indexes, those numbers are then concatenated as a string and finally converted to a QR code. Format created by SeedSigner, more info here.

    -

    Stackbit 1248

    -

    -

    -

    This metal backup format represents the BIP-39 mnemonic word's numbers (1-2048). Each of the four digits is converted to a sum of 1, 2, 4 or 8. This option does not print even if a printer driver is set.

    +
      +
    • Encrypted QR Code
    • +
    +

    This option converts the encrypted mnemonic into a QR code. Enter an encryption key and, optionally, a custom ID. When you scan this QR code through "Load Mnemonic" -> "Via Camera" -> "QR Code," you will be prompted to enter the decryption key to load the mnemonic stored in it. Like any QR code, it can be printed if a thermal printer driver is set up.

    -

    Tiny Seed

    -

    -

    -

    This metal backup format represents the BIP-39 mnemonic word's numbers (1-2048) in binary format on a metal plate, where the 1's are marked (punched) and the 0's are left intact.

    -
    - -

    Encrypt Mnemonic

    +

    Encrypted

    This feature allows you to back up your mnemonic by encrypting it and storing it on the device's flash memory, an SD card, or in QR code format. You can customize the encryption method and parameters in the settings.

    For convenience, you may choose to store the encrypted mnemonic on flash memory or an SD card, but it is advisable not to rely solely on these methods for backup. Flash storage can degrade over time and may be subject to permanent damage, resulting in the loss of stored information.

    When using any of the encryption methods, you will be prompted to enter an encryption key. This key can be provided in text or QR code format. Additionally, you have the option to set a custom ID for easier management of your mnemonics. If a custom key is not specified, the device's current loaded wallet fingerprint will be used as the ID.

    -

    Store on Flash

    +

    See this page to find out more about: Krux Mnemonics Encryption.

    +
      +
    • Store on Flash
    • +

    This option stores the encrypted mnemonic in the device's flash memory. You can decrypt and load it later through the "Load Mnemonic" -> "From Storage" option.

    -

    Store on SD Card

    +
      +
    • Store on SD Card
    • +

    If an SD card is available, this option stores the encrypted mnemonic on it. You can decrypt and load it later through the "Load Mnemonic" -> "From Storage" option.

    -

    Encrypted QR Code

    -

    This option converts the encrypted mnemonic into a QR code. When you scan this QR code through "Load Mnemonic" -> "Via Camera" -> "QR Code," you will be prompted to enter the decryption key to load the mnemonic stored in it.

    -

    Learn more about Krux Mnemonics Encryption

    +
      +
    • Encrypted QR Code +It's another path for the same functionality present on QR Code backups, described above.
    • +
    +

    Other Formats

    +
      +
    • Words + +
    • +
    +

    Display the BIP-39 mnemonic words as text so you can write them down.

    +
    + +
      +
    • Numbers + +
    • +
    +

    Display the BIP-39 mnemonic word numbers (1-2048) in decimal, hex, or octal format.

    +
    + +
    + +
      +
    • Stackbit 1248 + +
    • +
    +

    This metal backup format represents the BIP-39 mnemonic word's numbers (1-2048). Each of the four digits is converted to a sum of 1, 2, 4 or 8. This option does not print even if a printer driver is set.

    +
    + +
      +
    • Tiny Seed + +
    • +
    +

    This metal backup format represents the BIP-39 mnemonic word's numbers (1-2048) in binary format on a metal plate, where the 1's are marked (punched) and the 0's are left intact. You can also print your mnemonic in this format if a thermal printer driver is set.

    Extended Public Key

    -

    A menu will be presented with options to display your master extended public key (xPub) as text and as a QR code. Depending on whether a single-sig or multisig wallet was loaded, the options shown will be xPub, zPub, or ZPub. When displayed as text, the extended public key can be stored on an SD card if available. If you choose to export a QR code, you can not only scan it but also save it as an image on an SD card or print it if a thermal printer is attached.

    +

    A menu will be presented with options to display your master extended public key (xPub) as text and as a QR code. Depending on the script type or whether a single-sig or multisig wallet was loaded, the options shown will be xPub, yPub, zPub, or ZPub. When displayed as text, the extended public key can be stored on an SD card if available. If you choose to export a QR code, you can not only scan it but also save it as an image on an SD card or print it if a thermal printer is attached.

    -

    All QR codes will contain key origin information in key expressions. If your coordinator cannot parse this information, it will not be capable of importing the wallet's fingerprint. As a result, Krux will not be able to sign transactions created by it unless you manually add the fingerprint so that it can be used to create Krux compatible PSBTs.

    +

    All QR codes will contain key origin information in key expressions. If your coordinator cannot parse this information, it will not be capable of importing the wallet's fingerprint. As a result, Krux will not perform important verifications when signing transactions created by it unless you manually add the fingerprint so that it can be used to create Krux-compatible PSBTs.

    +

    Always prefer to import extended public keys directly from Krux when setting up a coordinator instead of copying it (or parts of it) from other sources.

    -

    Wallet Descriptor

    -

    When you select this option for the first time, you will be prompted to load a wallet. The camera will activate, and you will need to scan a wallet backup QR code generated by your wallet coordinator software. If the scan is successful, a preview of the wallet will be displayed for confirmation. If you abort the scan, you can alternatively load the wallet descriptor from an SD card.

    +

    Wallet

    +

    Here you can load view and save wallet descriptors, add or change passphrases, customize wallet's attributes, generate and load a BIP85 child mnemonic.

    +

    Wallet Descriptor

    +

    A Bitcoin Wallet Output Script Descriptor defines a set of addresses in a wallet. It includes the following information:

    +
      +
    • +

      Script Type: Specifies the type of script (e.g., P2PKH, P2SH, P2WPKH).

      +
    • +
    • +

      Origin Info: Defines the master fingerprint and derivation path used to derive keys.

      +
    • +
    • +

      Extended Public Keys: usually represented as an xpub, but could be ypub, zpub, etc.

      +
    • +
    +

    Output descriptors standardize how wallets generate addresses, ensuring compatibility and security. They help wallets and other software understand how to derive and verify the addresses used in transactions.

    +

    For multisig wallets, it is essential to load a descriptor to check addresses and perform full PSBT verification. For single-sig wallets, loading a descriptor is optional and serves as a redundancy check of the coordinator's wallet attributes.

    +

    When you select the "Wallet Descriptor" option for the first time, you will be prompted to load a wallet descriptor via QR code or SD card. After loading, a preview of the wallet attributes will be displayed for confirmation.

    -

    If you access this option again after having loaded your wallet, you will see the wallet's name, fingerprints and the abbreviated xPubs of all cosigners, along with a QR code containing the exact data that was initially loaded. If an SD card is inserted, you can save the descriptor to it. Additionally, if you have a thermal printer attached, you can print this QR code.

    -

    Please note that once a wallet is loaded, it cannot be changed. To load a different wallet, you will need to restart the device and re-enter your mnemonic.

    +

    If you access the "Wallet Descriptor" option again after loading your wallet, you will see the wallet's name, fingerprints, and the abbreviated XPUBs of all cosigners, along with a QR code containing the exact data that was initially loaded. If an SD card is inserted, you can save the descriptor to it for later use without the assistance of a coordinator. Additionally, if you have a thermal printer attached, you can print this QR code.

    +

    Krux also allows you to verify a descriptor's receive and change addresses without the need to load private keys. Simply turn on your Krux, access "Tools" -> "Descriptor Addresses," and load a trusted descriptor from a QR code or SD card.

    +

    Please note that if you customize the wallet parameters or restart the device, the descriptor will be unloaded, and you may need to load it again to check addresses.

    +
    + +

    Passphrase

    +

    +

    +

    If you forgot to load a passphrase while loading your wallet, or if you use multiple passphrases with the same mnemonic, you can add, replace, or remove a passphrase here. Simply choose between typing or scanning it.

    +

    To remove a passphrase, select "Type BIP39 Passphrase," leave the field blank, and press "Go."

    +

    Don't forget to verify the resulting fingerprint in the status bar to ensure you've loaded the correct key.

    +
    + +

    Customize

    +

    +

    +

    Here you are presented to the exact same customization options you have while loading a key and wallet. You can change the Network, Single/Multisig, Script Type and Account. More about wallet attributes

    +
    + +

    BIP85

    +

    Bitcoin BIP85, also known as the Deterministic Entropy From BIP32 Keychains, allows for the generation of deterministic entropy using a BIP32 master key. This entropy can then be used to create various cryptographic keys and mnemonics (e.g., BIP39 seed phrases). BIP85 ensures that all derived keys and mnemonics are deterministic and reproducible, meaning they can be recreated from the same master key. This feature is useful for securely managing multiple child keys from a single master key without the need to store each one separately.

    +

    + + +

    +

    Choose between a 12 or 24 words child then type the desired index to export a child mnemonic. After being presented to the new mnemonic, you can choose to load and use it right away.

    +

    Please note passphrases will be removed when loading a BIP85 child.

    Address

    @@ -1416,8 +1549,7 @@

    AddressScan Address

    -

    This option turns on the camera and allows you to scan in a QR code of a receive address. Upon scanning, it will render its own QR code of the address back to the display along with the (text) address below it. You could use this feature to scan the address of someone you want to send coins to and display the QR back to your wallet coordinator rather than copy-pasting an address.

    -

    If you have a thermal printer attached, you can also print this QR code.

    +

    This option turns on the camera and allows you to scan in a QR code of a receive address. Upon scanning, it will render its own QR code of the address back to the display along with the (text) address below it. You could use this feature to scan the address of someone you want to send coins to and display the QR back to your wallet coordinator rather than copy-pasting an address. If you have a thermal printer attached, you can also print this QR code.

    After proceeding through this screen, you will be asked if you want to check that the address belongs to your wallet. If you confirm, it will exhaustively search through as many addresses derived from your wallet as you want in order to find a match.

    This option exists as an extra security check to verify that the address your wallet coordinator has generated is authentic and belongs to your wallet.

    @@ -1425,13 +1557,13 @@

    Scan AddressReceive Addresses

    -

    List your wallet receiving addresses, you can browse to select an arbitrary address to show your QRCode and print if you want

    +

    List your wallet receiving addresses, you can browse to select an arbitrary address to show your QR code and print if you want.

    Change Addresses

    -

    List your wallet change addresses, you can browse to select an arbitrary address to show your QRCode and print if you want

    +

    List your wallet change addresses, you can browse to select an arbitrary address to show your QR code and print if you want.

    Sign

    @@ -1444,16 +1576,16 @@

    PSBT&par

    To sign a Bitcoin PSBT, you have the following options:

    -
      -
    1. Scan an Animated QR Code: Turn on the camera and scan an animated QR code of a PSBT generated by your wallet coordinator software.
    2. -
    3. Load from SD Card: Load an unsigned PSBT file from your SD card.
    4. -
    +

    Upon loading the PSBT, you will be presented with a preview showing the amount of BTC being sent, the recipient's address, and the transaction fee. Amounts are displayed according to your locale and the International Bureau of Weights and Measures, while still adhering to the concept of the Satcomma standard format.

    If you choose to proceed and sign the transaction, the signed PSBT can be exported in two ways:

    -
      -
    1. As an animated QR code, which can be scanned back into your coordinator wallet.
    2. -
    3. As a signed PSBT file, which can be saved to your SD card and then loaded back into your coordinator wallet for broadcasting.
    4. -
    +
      +
    • As an animated QR code, which can be scanned back into your coordinator wallet.
    • +
    • As a signed PSBT file, which can be saved to your SD card and then loaded back into your coordinator wallet for broadcasting.
    • +

    If a thermal printer is attached to your device, you can also print the PSBT QR codes for record-keeping or further processing.

    @@ -1524,13 +1656,13 @@

    Messages at Address + @@ -1607,7 +1739,13 @@
    Messages at Address{"base": "../../..", "features": ["navigation.tabs", "navigation.tabs.sticky", "navigation.sections", "navigation.top", "navigation.indexes", "navigation.expand", "navigation.footer", "toc.integrate"], "search": "../../../assets/javascripts/workers/search.b8dbb3d2.min.js", "translations": {"clipboard.copied": "Copied to clipboard", "clipboard.copy": "Copy to clipboard", "search.result.more.one": "1 more on this page", "search.result.more.other": "# more on this page", "search.result.none": "No matching documents", "search.result.one": "1 matching document", "search.result.other": "# matching documents", "search.result.placeholder": "Type to start searching", "search.result.term.missing": "Missing", "select.version": "Select version"}} - + + + + + + + diff --git a/en/getting-started/installing/from-test-release/index.html b/getting-started/usage/setting-a-coordinator-and-signing/index.html similarity index 64% rename from en/getting-started/installing/from-test-release/index.html rename to getting-started/usage/setting-a-coordinator-and-signing/index.html index d5a5a5d23..19b2c91a9 100644 --- a/en/getting-started/installing/from-test-release/index.html +++ b/getting-started/usage/setting-a-coordinator-and-signing/index.html @@ -10,28 +10,28 @@ - + - + - + - - + + - From pre-built test release - Krux - Open-source signing device firmware for Bitcoin + Setting a Coordinator and Signing - Krux - Open-source signing device firmware for Bitcoin - + - + @@ -50,9 +50,11 @@ - + - + + + @@ -76,7 +78,7 @@
    - + Skip to content @@ -92,9 +94,9 @@
    @@ -970,7 +1029,7 @@ - Devices and Part List + Devices and Parts List @@ -981,7 +1040,7 @@ - Devices and Part List + Devices and Parts List @@ -1044,6 +1103,15 @@ +
  • + +
  • + + + Maix Cube + + +
  • @@ -1071,9 +1139,9 @@ + +
  • @@ -1145,6 +1219,46 @@ +
  • + + + + + Troubleshooting + + + + +
  • + + + + + + + + + +
  • + + + + + Uncommon Questions + + + + +
  • + + + + + + + + +
  • @@ -1177,7 +1291,7 @@ -

    Devices and Part List

    +

    Devices and Parts List

    Krux Compatible Devices

    @@ -1186,21 +1300,23 @@

    Comparative TableComparative Table 2.4" / 240*320 2" / 240*320 +1.3" / 240*240 + + +Brightness control +✅ +❌ +❌ +❌ +❌ +✅ + + +Device size +48*24*22mm +104*63*17mm +98*59*18mm +69*84*41mm +57*41*17mm +40*40*16mm Touchscreen @@ -1217,14 +1352,16 @@

    Comparative Table ❌ Capacitive +❌ -Camera -OV7740 -OV7740 rear
    GC0328 front -GC0328 -OV2640 or
    OV5642 -OV2640 +Camera
    +OV7740 +OV7740 rear
    GC0328 front +GC0328 +OV2640 or
    OV5642 +OV2640 (VER:1.0) or
    GC2145 (VER:1.1) +OV7740 Battery @@ -1233,6 +1370,7 @@

    Comparative Table ❌ ❌ +200mAh Requirements @@ -1241,37 +1379,42 @@

    Comparative TableRotary encoder
    3D printed case
    Soldering
    Assembly Buttons
    3D printed case
    Soldering
    Assembly None +None Warnings - - - + + + Camera has
    lens distortion Micro USB +3-Way button -

    : -M5stickV's USB-C port lacks pull up resistors required for it to be recognized and powered by host (computer) USB-C ports. If you don't have an USB-A available, you can use a USB hub connected between your computer's USB-C and M5stickV. +

    : +Only OV7740 and OV2640 have an anti-glare mode to better capture images from high brightness screens or with incident light.

    -

    : -Some Amigo screens have inverted X coordinates, others display colors differently. For more info see FAQ +

    : +M5StickV's USB-C port lacks pull up resistors required for it to be recognized and powered by host (computer) USB-C ports. If you don't have an USB-A available, you can use a USB hub connected between your computer's USB-C and M5StickV.

    -

    : -Some stores ship the Maix Dock with soldered pin connectors that do not fit into the 3D printed case +

    : +Some Amigo screens have inverted X coordinates, others display colors differently. For more info see Troubleshooting. +

    +

    : +Some stores ship the Maix Dock with soldered pin connectors that do not fit into the 3D printed case.

    All devices feature Kendryte K210 chip: 28nm process, dual-core RISC-V 64bit @400MHz, 8 MB high-speed SRAM, DVP camera and MCU LCD interface, AES Accelerator, SHA256 Accelerator, FFT Accelerator.

    M5StickV

    -

    +

    Below is a list of some distributors where you can find this device:

  • Maix Amigo

    -

    +

    Below is a list of some distributors where you can find this device:

    Yahboom k210 module

    -

    -

    Below is a list of some distributors where you can find this device:

    +

    +

    It comes with a compatible 32G card, an USB card reader, one PH2.0 4Pin male-to-male connector and one PH2.0 female adapter (to connect to a thermal printer). Below is a list of some distributors where you can find this device:

    +

    Maix Cube

    +

    +

    Below is a list of some distributors where you can find this device:

    + +
    +

    Maix Dock and Maix Bit

    -

    +

    For the DIYers, the Maix Dock and Maix Bit are also supported but will require sourcing the parts individually and building the device yourself.

    Below are example implementations with instructions on how to recreate them:

      @@ -1322,16 +1478,17 @@

      Maix Dock and Maix Bit

    Other Parts

    -

    USB-C Charge Cable

    -

    This will be included with the M5StickV and Maix Amigo that you purchase from one of the distributors above. It will be necessary to power and charge the device and to initially flash the firmware.

    +

    USB-C or Micro USB Charge Cable

    +

    This will come with the device. It will be necessary to power, charge the device (if it has battery) and to initially flash the firmware.

    (Optional) MicroSD Card

    We cannot guarantee that a microSD card is compatible and will work in your device; you'll need to test it on the device to be sure, read the FAQ for more info. -The size of the SD card isn't important; anything over a few megabytes will be plenty.

    -

    (Optional) Thermal Printer

    +Yahboom will come with a compatible 32G card. The size of the SD card isn't important; anything over a few megabytes will be plenty.

    +

    (Optional) TTL Serial Thermal Printer

    +

    Warning/Disclaimer: This feature is intended for individuals with experience in electronics tinkering and soldering.

    Krux has the capability to print all QR codes it generates, including those for mnemonics, xpubs, wallet backups, and signed PSBTs, using a locally-connected thermal printer via its serial port.

    -

    Many thermal printers may be compatible, but currently, the Goojprt QR203 (easily found on AliExpress) has the best support. The Adafruit printer starter pack can also be a convenient option to get started, as it includes all the necessary components for printing (except the conversion cable). To ensure proper functionality, enable the printer driver in the Krux settings, set the Tx pin and baud rate value to either 19200 or 9600, as explained in this Adafruit printer tutorial. You will need to connect the device's Tx to the printer's Rx and ground. The printer requires a dedicated power supply, typically with an output of 5 to 9V and capable of supplying at least 2A. For more information, see this discussion.

    -

    (Optional) Conversion Cable for Thermal Printer

    -

    To connect the printer to the device, you will need a conversion cablewith a 4-pin female Grove connector on one end (to connect to the device) and 4-pin male jumpers on the other end (to connect to the printer). For a more reliable connection, it is recommended to cut and solder the wires of your custom cables instead of using jumpers.

    +

    Many TTL serial thermal printers may be compatible, but currently, the Goojprt QR203 has the best support. The Adafruit printer starter pack can also be a convenient option to get started, as it includes all the necessary components for printing (except the conversion cable). To ensure proper functionality, enable the printer driver in the Krux settings, set the Tx pin and baud rate value to either 19200 or 9600 (depends on the printer), as explained in this Adafruit printer tutorial. You will need to connect the device's Tx to the printer's Rx and device's ground to the printer's ground, do not connect any other pins because a wrong connection may damage your device. The printer requires a dedicated power supply, typically with an output of 5 to 9V and capable of supplying at least 2A. For more information, see this discussion.

    +

    (Optional) Conversion Cable for Thermal Printer

    +

    To connect the printer to M5StickV, Amigo or Cube, you will need a grove conversion cable with a 4-pin male Grove connector on one end (to connect to the device) and 4-pin male jumpers on the other end (to connect to the printer). Check your device and printer model connection first, Yahboom comes with PH2.0 4Pin female connector; Dock and Bit doesn't have a connector. For a more reliable connection, it is recommended to cut and solder the wires of your custom cables instead of using jumpers.

    @@ -1425,7 +1582,7 @@

    (Optional) Conversion Cab - + @@ -1433,15 +1590,15 @@

    (Optional) Conversion Cab - + - - + + @@ -1449,7 +1606,7 @@

    (Optional) Conversion Cab - +

    @@ -1467,7 +1624,13 @@

    (Optional) Conversion Cab - + + + + + + + diff --git a/search/search_index.json b/search/search_index.json index 6a8d5b859..773e816a6 100644 --- a/search/search_index.json +++ b/search/search_index.json @@ -1 +1 @@ -{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"faq/","title":"FAQ","text":""},{"location":"faq/#why-are-the-buttons-on-my-maix-amigo-in-the-wrong-order-why-is-my-amigo-screen-displaying-the-wrong-colors","title":"Why are the buttons on my Maix Amigo in the wrong order? Why is my Amigo screen displaying the wrong colors?","text":"

    Some Amigo screens have inverted X coordinates while others don\u2019t. If you notice that the buttons on keypad input screens appear to be in the wrong order, please go to Settings > Hardware > Display and change the value of Flipped X Coordinates which should correct the issue.

    Others have found that there are issues with the colors displayed in the interface and camera preview. To fix this we have two options in Settings > Hardware > Display, BGR Colors and Inverted Colors, test with them until the colors appear to be correct on your device.

    "},{"location":"faq/#why-doesnt-my-maix-amigo-touchscreen-work-with-v24030-if-it-worked-fine-with-v23091","title":"Why doesn't my Maix Amigo touchscreen work with v24.03.0 if it worked fine with v23.09.1?","text":"

    We added IRQ to the firmware, so when you open your Maix Amigo, you will see a switch in the middle of the device board, it must be in the upper position for the touchscreen to work with v24.03.0 and later.

    "},{"location":"faq/#why-isnt-my-device-charging-or-being-recognized-when-connected-to-the-computers-usb","title":"Why isn't my device charging or being recognized when connected to the computer's USB?","text":"

    If you have a Maix Amigo, make sure you're using the USB-C port at the bottom of the device, not the one on the left side.

    Different computer hosts have varying hardware, operating systems, and behaviors regarding their USB ports. Below are the expected behaviors:

    "},{"location":"faq/#usb-a","title":"USB-A:","text":"

    Your device should charge and turn on when connected to a USB-A port, even if it was initially turned off. You can also turn off the device while it continues to charge. However, some hosts' USB-A ports may behave like USB-C ports, as described below.

    "},{"location":"faq/#usb-c","title":"USB-C:","text":"
    • If the device is turned off and connected to a USB-C port, it should turn on and start charging. You can turn it off again, and it will continue to charge.

    • If the device is already turned on and connected to a USB-C port, it may not charge or be recognized by the computer. In this case, turn off the device to initiate recognition and charging. Once turned off and reconnected, the device should restart, be recognized by the computer, and charging should be triggered by USB-C hosts. If your device is not charging or being recognized as expected, try using a different USB port or a different computer to determine if the issue is with the device or the host's USB port.

    "},{"location":"faq/#why-isnt-my-m5stickv-device-being-recognized-and-charged-when-connected-to-the-computers-usb-c","title":"Why isn't my M5stickV device being recognized and charged when connected to the computer's USB-C?","text":"

    M5stickV's USB-C port lacks pull up resistors required for it to be recognized and powered by host (computer) USB-C ports. If you don't have an USB-A available, you can use a USB hub connected between your computer's USB-C and M5stickV.

    "},{"location":"faq/#why-does-my-krux-device-randomly-freeze-or-restart-when-connected-to-the-computer","title":"Why does my Krux device randomly freeze or restart when connected to the computer?","text":"

    Windows is known to have issues with the USB-C devices. If you are experiencing random crashes or even reboots and your device does not have a battery, try using a phone charger or other power source such as a power bank.

    "},{"location":"faq/#why-wont-my-linux-os-list-a-serial-port-after-connecting-my-device","title":"Why won't my Linux OS list a serial port after connecting my device?","text":"

    If you get the following error when trying to flash your device: Failed to find device via USB. Is it connected and powered on? Make sure your device is being detected and serial ports are being mounted by running:

    ls /dev/ttyUSB*\n
    Expect one port to be listed for devices like M5stickV and Maix Dock /dev/ttyUSB0, and two ports for Maix Amigo and Maix Bit /dev/ttyUSB0 /dev/ttyUSB1.

    If you don't see them, your OS may not be loading the correct drivers to create the serial ports to connect to. Ubuntu has a known bug where the brltty driver \"kidnaps\" serial devices. You can solve this problem by removing it:

    sudo apt-get remove brltty\n

    "},{"location":"faq/#my-device-didnt-reboot-after-flashing-the-firmware-and-when-i-turned-it-off-and-on-again-it-just-stayed-blank-without-showing-anything-on-the-screen-what-should-i-do","title":"My device didn't reboot after flashing the firmware and when I turned it off and on again, it just stayed blank without showing anything on the screen. What should I do?","text":"

    Check if the downloaded file matches the device, this can also occur due to data corruption. Try downloading binaries again. You can install MaixPy IDE to help with debugging, Tools > Open Terminal > New Terminal > Connect to serial port > Select a COM port available (if it doesn't work, try another COM port). It will show the terminal and some messages, a message about an empty device or with corrupted firmware appears like: \"interesting, something's wrong, boot failed with exit code 233, go to find your vendor.\"

    "},{"location":"faq/#what-are-all-the-features-available-what-are-the-additional-features-of-the-test-or-beta-version-is-there-an-android-app","title":"What are all the features available? What are the additional features of the Test or Beta version? Is there an Android app?","text":"

    For official releases you will find all the features detailed here on the Getting Started page with a brief summary on the Navigation Overview page. The latest and most experimental features, which we sometimes share on our social media, can be found only in the test (beta) repository. Only official releases are signed, Test or Beta is just for trying new things and providing feedback. Krux Android app is available as an apk on the test (beta) repository (requires Android 6.0 or above).

    "},{"location":"faq/#why-does-krux-show-an-xpub-for-a-segwit-address","title":"Why does Krux show an xpub for a segwit address?","text":"

    The xpub that Krux displays follows the bitcoin core descriptors spec and includes key origin and derivation info that, in theory, makes zpubs (and ypubs) unnecessary if the wallet software being shown this extra information can parse it.

    From the spec:

    Every public key can be prefixed by an 8-character hexadecimal fingerprint plus optional derivation steps (hardened and unhardened) surrounded by brackets, identifying the master and derivation path the key or xpub that follows was derived with.

    However, in practice not all wallet software supports this extended format, so Krux still provides a zpub as a fallback.

    For more information, check out https://outputdescriptors.org/.

    "},{"location":"faq/#why-isnt-krux-scanning-my-qr-code","title":"Why isn't Krux scanning my QR code?","text":"

    The level of detail that you see is what Krux sees. If the QR code shown on the device's screen is blurry, the camera lens of the device may be out of focus. It can be adjusted by rotating it clockwise or counter-clockwise to achieve a clearer result. The lenses usually comes with a drop of glue that makes id harder to adjust for the first time. You can use your fingertip, tweezers or small precision pliers to help, being careful to don't damage the fragile lenses.

    If you have adjusted the lens already, the device may be too far away or too close to the code to read it. Start by holding the device as close to the QR code as possible and pulling away slowly until all or most of the QR code is viewable within the screen. If the code on the screen looks crisp, Krux should read it quickly and give you immediate feedback.

    If you are in a dark environment, you can hold down the ENTER button of the M5StickV or Maix Amigo to turn on their LED light to potentially increase visibility. M5stickV and Amigo also has an anti-glare mode to better capture images from high brightness screens or with incident light, to enable/disable the anti-glare just press the PAGE button while scanning.

    "},{"location":"faq/#why-am-i-getting-an-error-when-i-try-to-scan-a-qr-code","title":"Why am I getting an error when I try to scan a QR code?","text":"

    If Krux is recognizing that it sees a QR code but is displaying an error message after reading it, the likely reason is that the QR code is not in a format that Krux works with. We have listed the supported formats below:

    For BIP-39 mnemonics:

    1. BIP-39 Plaintext (Used by Krux and https://iancoleman.io/bip39/)
    2. SeedSigner SeedQR and CompactSeedQR Formats
    3. UR Type crypto-bip39
    4. Encrypted QR Code (Format created by Krux, more info here)

    For Wallet output descriptor:

    1. JSON with at least a descriptor key containing an output descriptor string
    2. Key-value INI files with at least Format, Policy, and Derivation keys
    3. UR Type crypto-output

    For PSBT (Partially Signed Bitcoin Transactions):

    1. Base43, Base58, and Base64-encoded bytes
    2. Raw Bytes
    3. UR Type crypto-psbt

    Additionally, Krux recognizes animated QR codes that use either the plaintext pMofN (the Specter QR format) or binary UR encodings.

    "},{"location":"faq/#why-cant-my-computer-read-the-qr-code-that-krux-displays","title":"Why can't my computer read the QR code that Krux displays?","text":"

    If you are using an M5StickV, the small screen makes it difficult for laptop webcams to capture enough detail to parse the QR codes it displays. You can toggle brightness of QR codes from public keys and PSBTs by pressing PAGE button. In the future, more work will be done to support displaying lower density QR codes. For now, a workaround you can do is to take a picture or video of the QR code with a better-quality camera (such as your phone), then enlarge and display the photo or video to your webcam. Alternatively, it may be simpler to use a mobile wallet such as BlueWallet with the M5StickV since phone cameras don't seem to have issues reading the small QR codes. You can also save the PSBT on a microSD card for Krux to sign and then save the signed transaction to the microSD card to transfer the file to the computer or phone.

    "},{"location":"faq/#why-isnt-krux-detecting-my-microsd-card-or-presenting-an-error","title":"Why isn't Krux detecting my microSD card or presenting an error?","text":"

    Starting from version 23.09.0, Krux supports SD card hot plugging. If you are using older versions, it may only detect the SD card at boot, so make sure Krux is turned off when inserting the microSD into it. To test the card compatibility use Krux Tools>Check SD Card. Make sure the SD card is using MBR/DOS partition table and FAT32 format.

    Here is some supported microSD cards, and here is the MaixPy FAQ explaining Why my micro SD card cannot be read.

    "},{"location":"faq/#why-insert-an-sd-card-into-my-device-what-is-it-for-does-it-save-something","title":"Why insert an SD card into my device? What is it for? Does it save something?","text":"

    SD card use is optional, most people use Krux only with QR codes. But you can use SD card to to upgrade the firmware, save settings, cnc/file, QR codes, XPUBs, encrypted mnemonics, and to save and load PSBTs, messages and wallet output descriptors.

    "},{"location":"","title":"Krux","text":"

    Krux is an open-source firmware that transforms off-the-shelf Kendryte K210 devices, such as the Maix Amigo, M5StickV and more, into versatile Bitcoin transaction signers. Beyond its core functionality, Krux is a flexible platform that can adapt to devices with different form factors, providing a suite of tools to assist with the creation and recovery of mnemonic backups, some of which include encryption options for enhanced security.

    Devices like Maix Amigo comes ready to use, with large touchscreens that make it easy and user-friendly to operate. These devices are ideal for those looking for a plug-and-play solution. On the other hand, Krux also supports development board kits, which are perfect for DIY enthusiasts who enjoy customizing and building their own hardware setups.

    Interacting seamlessly with leading coordinator wallets through QR codes, SD cards, and even thermal printers, the firmware is continuously evolving to become a Swiss Army knife for self-custody, offering an array of features to support transactions and backups in a user-friendly and airgapped environment.

    To learn more about Krux, check out Getting Started.

    "},{"location":"parts/","title":"Devices and Part List","text":""},{"location":"parts/#krux-compatible-devices","title":"Krux Compatible Devices","text":""},{"location":"parts/#comparative-table","title":"Comparative Table","text":"Device M5stickV Maix Amigo Maix Dock Maix Bit Yahboom k210 module Price avg. US$50 US$55 US$35 US$35 US$60 Screen size / resolution 1.14\" / 135*240 3.5\" / 320*480 2.4\" / 240*320 2.4\" / 240*320 2\" / 240*320 Touchscreen Capacitive Capacitive Camera OV7740 OV7740 rearGC0328 front GC0328 OV2640 orOV5642 OV2640 Battery 200mAh 520mAh Requirements None None Rotary encoder 3D printed case SolderingAssembly Buttons 3D printed case SolderingAssembly None Warnings Camera has lens distortion Micro USB

    : M5stickV's USB-C port lacks pull up resistors required for it to be recognized and powered by host (computer) USB-C ports. If you don't have an USB-A available, you can use a USB hub connected between your computer's USB-C and M5stickV.

    : Some Amigo screens have inverted X coordinates, others display colors differently. For more info see FAQ

    : Some stores ship the Maix Dock with soldered pin connectors that do not fit into the 3D printed case

    All devices feature Kendryte K210 chip: 28nm process, dual-core RISC-V 64bit @400MHz, 8 MB high-speed SRAM, DVP camera and MCU LCD interface, AES Accelerator, SHA256 Accelerator, FFT Accelerator.

    "},{"location":"parts/#m5stickv","title":"M5StickV","text":"

    Below is a list of some distributors where you can find this device:

    • M5Stack
    • Mouser
    • Digi-Key
    • Elektromaker
    • Lee's Electronic
    • AliExpress
    • ABRA
    • Adafruit
    • Cytron
    "},{"location":"parts/#maix-amigo","title":"Maix Amigo","text":"

    Below is a list of some distributors where you can find this device:

    • AliExpress
    • Seeed Studio
    • Digi-Key
    • Mouser
    • Electromaker
    "},{"location":"parts/#yahboom-k210-module","title":"Yahboom k210 module","text":"

    Below is a list of some distributors where you can find this device:

    • AliExpress
    • Amazon
    • Yahboom Store
    • ETC HK Shop
    "},{"location":"parts/#maix-dock-and-maix-bit","title":"Maix Dock and Maix Bit","text":"

    For the DIYers, the Maix Dock and Maix Bit are also supported but will require sourcing the parts individually and building the device yourself.

    Below are example implementations with instructions on how to recreate them:

    • https://github.com/selfcustody/DockEncoderCase
    • https://github.com/selfcustody/MaixBitCase

    Below is a list of some distributors where you can find these devices:

    • Mouser
    • Electromaker
    • Digi-Key
    • AliExpress
    • Amazon
    "},{"location":"parts/#other-parts","title":"Other Parts","text":""},{"location":"parts/#usb-c-charge-cable","title":"USB-C Charge Cable","text":"

    This will be included with the M5StickV and Maix Amigo that you purchase from one of the distributors above. It will be necessary to power and charge the device and to initially flash the firmware.

    "},{"location":"parts/#optional-microsd-card","title":"(Optional) MicroSD Card","text":"

    We cannot guarantee that a microSD card is compatible and will work in your device; you'll need to test it on the device to be sure, read the FAQ for more info. The size of the SD card isn't important; anything over a few megabytes will be plenty.

    "},{"location":"parts/#optional-thermal-printer","title":"(Optional) Thermal Printer","text":"

    Krux has the capability to print all QR codes it generates, including those for mnemonics, xpubs, wallet backups, and signed PSBTs, using a locally-connected thermal printer via its serial port.

    Many thermal printers may be compatible, but currently, the Goojprt QR203 (easily found on AliExpress) has the best support. The Adafruit printer starter pack can also be a convenient option to get started, as it includes all the necessary components for printing (except the conversion cable). To ensure proper functionality, enable the printer driver in the Krux settings, set the Tx pin and baud rate value to either 19200 or 9600, as explained in this Adafruit printer tutorial. You will need to connect the device's Tx to the printer's Rx and ground. The printer requires a dedicated power supply, typically with an output of 5 to 9V and capable of supplying at least 2A. For more information, see this discussion.

    "},{"location":"parts/#optional-conversion-cable-for-thermal-printer","title":"(Optional) Conversion Cable for Thermal Printer","text":"

    To connect the printer to the device, you will need a conversion cablewith a 4-pin female Grove connector on one end (to connect to the device) and 4-pin male jumpers on the other end (to connect to the printer). For a more reliable connection, it is recommended to cut and solder the wires of your custom cables instead of using jumpers.

    "},{"location":"support/","title":"Support the Project","text":""},{"location":"support/#ways-you-can-help","title":"Ways you can help","text":""},{"location":"support/#development","title":"Development","text":"

    Audit the code, file an issue, make a pull request, or do all three. :)

    "},{"location":"support/#documentation","title":"Documentation","text":"

    I'd like to see Krux help as many people as possible, and to do that, good documentation is needed. If you identify a better way to say something, please make a PR, any help is appreciated.

    "},{"location":"support/#translation","title":"Translation","text":"

    Krux supports different languages. If you missed a language or saw a awkward translation, open an issue or make a PR! You can also make a difference by translating this documentation!

    "},{"location":"support/#social","title":"Social","text":"

    Reach out our Telegram group or X profile for faster help, share ideas and join the Krux community. Help others get to know Krux.

    \"I'm an engineer, not a marketer. If you like Krux, help spread the word!\" - Jeff

    "},{"location":"support/#donations","title":"Donations","text":"

    Krux won't ask for, receive, manage or distribute donations. You can donate directly to a Krux contributor of your choice.

    Thank you!

    "},{"location":"getting-started/","title":"Getting Started","text":"

    Krux is open-source Bitcoin signing firmware for devices with the K210 chipset.

    Signing operations in Krux are done offline via QR code or via SD card. You can create/load your BIP-39 mnemonic, or import a wallet output descriptor, and sign transactions all without having to plug the device into your computer (except to initially install the firmware). It reads QR codes with its camera and outputs QR codes to its screen, or to paper via an optional thermal printer attachment.

    Krux does not come with its own desktop wallet software. Instead, you can use Krux with third-party wallet coordinators to create/manage wallets, and send transactions from your online computer or mobile device while keeping your keys offline. Krux was built to be vendor agnostic and works with many popular wallet coordinators, including:

    • Sparrow Wallet
    • Nunchuk
    • Specter Desktop
    • BlueWallet
    "},{"location":"getting-started/navigation/","title":"Navigation Overview","text":"

    Below is the mind map representation of the currently menus available. Click the circle with a number (Ex.: ) to the right of each node to expand and explore. Also, enable full screen in the top right menu for better viewing .

    "},{"location":"getting-started/navigation/#login-menu","title":"Login Menu","text":""},{"location":"getting-started/navigation/#home-menu-loaded-a-mnemonic","title":"Home Menu (Loaded a mnemonic)","text":""},{"location":"getting-started/settings/","title":"Settings","text":"

    In the Krux home menu, there is a Settings entry. Below is a breakdown of the options you can change:

    "},{"location":"getting-started/settings/#bitcoin-network","title":"Bitcoin - Network","text":"

    This option allows you to switch between mainnet (the default) and testnet. Testnet can be used to try out different wallet coordinators or for development.

    "},{"location":"getting-started/settings/#encryption","title":"Encryption","text":"

    Modify the encryption method and parameters to fit your needs. This will be used when storing encrypted mnemonics or creating encrypted QR codes. For more info see Krux Encrypted Mnemonics.

    "},{"location":"getting-started/settings/#pbkdf2-iter-iterations","title":"PBKDF2 Iter. (Iterations)","text":"

    When you enter the encryption key, it is not directly used to encrypt your data. In order to protect against brute force attacks, the key is derived multiple times using hashing functions. PBKDF2 (Password-Based Key Derivation Function) iterations stands for the amount of derivations that will be performed over your key prior to encrypt/decrypt your mnemonic.

    If you increase this value it will make the encryption harder, at the cost of taking longer to encrypt/decrypt your mnemonics.

    Values must be multiple of 10,000. This was done to save data space on QR codes.

    "},{"location":"getting-started/settings/#encryption-mode","title":"Encryption Mode","text":"

    Choose between well known and widely used AES (Advanced Encryption Standard) modes:

    "},{"location":"getting-started/settings/#aes-ecb","title":"AES-ECB","text":"

    ECB (Electronic Codebook), its a simpler method where encryption data blocks are encrypted individually. It will be faster and simpler to encrypt, QR codes will have a lower density and will be easier to transcribe.

    "},{"location":"getting-started/settings/#aes-cbc","title":"AES-CBC","text":"

    CBC (Cipher-block Chaining) is considered more secure as in the first data block an initialization vector (IV) is used to add random data to the encryption. The encryption of subsequent blocks depends on the data from previous blocks, ensuring chaining.

    Encryption will take longer because a snapshot will be needed to generate the IV. This IV will be stored together with encrypted data, making encrypted QR codes denser and harder to transcribe.

    "},{"location":"getting-started/settings/#hardware","title":"Hardware","text":"

    Customize the parameters available for your device and change printer settings.

    "},{"location":"getting-started/settings/#encoder-maix-dock-only","title":"Encoder (Maix Dock only)","text":"

    If your device has a rotary encoder, you can change the debounce threshold in milliseconds. With lower values, faster movements and navigation will be allowed.

    The caveat is low values can cause issues, such as double step and unexpected movements, especially with lower quality encoders. If this is the case increase the value to make navigation more stable.

    "},{"location":"getting-started/settings/#display-maix-amigo-only","title":"Display (Maix Amigo only)","text":"

    Some Maix Amigo screens are different, here you can customize the BGR Colors, Flipped X Coordinates and Inverted Colors. For more info see FAQ

    "},{"location":"getting-started/settings/#printer","title":"Printer","text":"

    You can set up a thermal printer or tell Krux to store a GRBL CNC instructions file on a SD card to machine QR codes

    "},{"location":"getting-started/settings/#cnc","title":"CNC","text":"

    Define several machining parameters according to the desired size, material you'll use, and your CNC characteristics and capabilities.

    "},{"location":"getting-started/settings/#thermal","title":"Thermal","text":"

    Printers can come with different baudrates from the manufacturer. By default, Krux assumes the connected printer will have a baudrate of 9600. If yours is different, you can change it here.

    Also setup the TX Pin you'll use (e.g. 35 for M5stickV and 7 for Maix Amigo) and tweak other parameters according to your printer recommendations. For most printers you will only need to connect 2 cables, the device TX to the printer RX and ground. Consult the part list page for supported printers.

    "},{"location":"getting-started/settings/#driver","title":"Driver","text":"

    Here you choose between Thermal, CNC or none (default). Leave this setting to \"none\" if you won't use a printer and don't want to be bothered by print prompts.

    "},{"location":"getting-started/settings/#touchscreen-maix-amigo-and-yahboom-only","title":"Touchscreen (Maix Amigo and Yahboom only)","text":"

    If your device has touchscreen you can change the touch detection threshold. If it is being too sensitive or detecting false or ghost touches, you should increase the threshold value, making it less sensitive. The other way is also valid, reduce the threshold to make the screen more sensitive to touches.

    "},{"location":"getting-started/settings/#language-locale","title":"Language - Locale","text":"

    Here you can change Krux to your desired language.

    "},{"location":"getting-started/settings/#persist","title":"Persist","text":"

    Choose between flash (device's internal memory) or SD card for the place where your settings will be stored.

    "},{"location":"getting-started/settings/#appearance","title":"Appearance","text":"

    Configure screensaver time and change Krux to your desired theme.

    "},{"location":"getting-started/settings/#screensaver-time","title":"Screensaver time","text":"

    Set how long to wait idle before the screensaver appears. Enter 0 to disable the screensaver.

    "},{"location":"getting-started/settings/#theme","title":"Theme","text":"

    Choose your color theme according to your preference. Some themes may be more suitable for some devices, coordinator cameras and environments. As an example, it may be easier to scan QR codes from Krux devices using light theme in brighter environments.

    "},{"location":"getting-started/settings/#factory-settings","title":"Factory Settings","text":"

    Restore device to factory settings and reboot.

    "},{"location":"getting-started/features/QR-transcript-tools/","title":"Transcribing QR Codes","text":"

    When you export a mnemonic, encrypted mnemonic or a generic text QR code, alternative visualization modes will be available. Swipe left or right to change modes, or if your device doesn't have a touchscreen, press the Page buttons. Find transcribe templates here.

    "},{"location":"getting-started/features/QR-transcript-tools/#standard-mode","title":"Standard Mode","text":"

    This mode is optimized for scanning, the raw QR code will be displayed

    "},{"location":"getting-started/features/QR-transcript-tools/#lines-mode","title":"Lines Mode","text":"

    If you are good at transcribing things like handwritten text, with this mode one QR code line will be highlighted at a time. Press Enter to highlight the next line.

    "},{"location":"getting-started/features/QR-transcript-tools/#zoomed-regions-mode","title":"Zoomed Regions Mode","text":"

    QR codes will be split into regions, of 5x5 or 7x7 \"blocks\". One QR code region will be shown at a time. Press Enter to display the next region.

    "},{"location":"getting-started/features/QR-transcript-tools/#highlighted-regions-mode","title":"Highlighted Regions Mode","text":"

    QR codes will be split into regions, of 5x5 or 7x7 \"blocks\". One QR code region will be highlighted at a time. Press Enter to highlight the next region.

    "},{"location":"getting-started/features/QR-transcript-tools/#grided-mode","title":"Grided Mode","text":"

    Grids will be added to a standard QR code. In a dark room, if you place a sheet of paper over the device's screen, you'll notice QR code will be visible and it will be possible to copy it directly from above. Be careful to don't damage your screen with pen and markers, use an insulating plastic tape or film to protect the device if you use this method.

    "},{"location":"getting-started/features/encrypted-mnemonics/","title":"Encrypted Mnemonics","text":""},{"location":"getting-started/features/encrypted-mnemonics/#introduction","title":"Introduction","text":"

    There are many possible security layers one could add to protect a wallet\u2019s private key. Adding a BIP-39 passphrase to the mnemonic is the most common method. Encrypting a BIP-39 mnemonic has a similar use case as the BIP-39 passphrase, but the user experience may differ depending on the implementation. The main difference between BIP-39 passphrases and Krux\u2019s encrypted mnemonic implementation is that when users type the wrong key, encrypted mnemonics will return an error instead of loading a different wallet, as BIP-39 passphrases do. This difference may be desired or not. The implementation also has the convenience of storing a mnemonic ID together with the stored or QR code encrypted mnemonics. Mnemonic encryption, with its own key, can be used together with BIP-39 passphrase as an extra security layer.

    We use standard AES encryption modes ECB and CBC:

    "},{"location":"getting-started/features/encrypted-mnemonics/#aes-ecb","title":"AES-ECB","text":"

    ECB (Electronic Codebook) is a simpler method where encryption data blocks are encrypted individually. This mode is faster and simpler to encrypt, resulting in QR codes with lower density and easier to transcribe. It is generally considered less secure than CBC because it does not provide data chaining, meaning identical plaintext blocks will produce identical ciphertext blocks, making it vulnerable to pattern analysis. However, in Krux's implementation, only one or two binary data blocks are encrypted, so there will be no patterns, and the lack of chaining is not as relevant as it would be for larger files, plain text, or media.

    "},{"location":"getting-started/features/encrypted-mnemonics/#aes-cbc","title":"AES-CBC","text":"

    CBC (Cipher-block Chaining) is considered more secure. In the first data block, an initialization vector (IV) is used to add random data to the encryption. The encryption of subsequent blocks depends on the data from previous blocks, characterizing chaining. The tradeoff is that the encryption process will take longer because a snapshot will be needed to generate the IV. This IV will be stored together with encrypted data, making encrypted QR codes denser and harder to transcribe.

    "},{"location":"getting-started/features/encrypted-mnemonics/#cbc-encryption-iv","title":"CBC Encryption IV","text":"

    The Initial Vector (IV) will be generated from a snapshot taken with the camera. The IV is a fixed-size input value used in the first block of the encryption process. It adds randomness to the encryption, ensuring that data encrypted with the same key will produce different ciphertexts each time. The IV is not secret and will be transmitted along with the ciphertext. However, like any nonce, it should not be reused to maintain security.

    "},{"location":"getting-started/features/encrypted-mnemonics/#pbkdf2-iterations","title":"PBKDF2 Iterations","text":"

    When you enter the encryption key, it is not directly used to encrypt your data. In order to protect against brute force attacks, the key is derived multiple times using hashing functions. PBKDF2 (Password-Based Key Derivation Function) iterations refer to the number of derivations that will be performed over your key prior to encrypting/decrypting your mnemonic.

    "},{"location":"getting-started/features/encrypted-mnemonics/#encrypted-qr-codes-data-and-parsing","title":"Encrypted QR Codes Data and Parsing","text":"

    In search of efficiency and smaller QR codes, all data is converted to bytes and organized like a Bitcoin transaction, with variable and fixed length fields. The following data is present on the QR code:

    ID length (1) ID (2) Version (3) Key Derivations (4) IV (5) Encrypted Mnemonic (6) Validation Block (7) 1 Byte Variable 1 Byte 3 Bytes 16 Bytes (optional) 16 Bytes (12 words) 32 Bytes (24 words) 16 Bytes
    • Visible data (1 to 4):
      • (1) Mnemonic ID length (1 Byte).
      • (2) Mnemonic ID (variable lenght): Custom ID or wallet fingerprint.
      • (3) Version (1 Byte): Version of encryption method, currently two are available:
        • 0: AES-ECB-PBKDF2: Electronic Codebook with PBKDF2 key derivation.
        • 1: AES-CBC-PBKDF2: Cypher Block Chaining with PBKDF2 key derivation.
      • (4) Key derivation iterations (3 Bytes): Number of PBKDF2 key derivations times 10,000.
    • Cipher data (5 to 7):
      • (5) IV (16 Bytes-optional): Initial vector for AES-CBC encryption, possibility to be nonce for future AES-CTR or other encryption methods.
      • (6) Encrypted Mnemonic (16 Bytes - 12 words, 32 Bytes - 24 words): Mnemonic ciphertext.
      • (7) Validation block (16 Bytes): Currently using first 16 bytes of sha256 of the mnemonic bytes as checksum, could be used in future to store AES-AEX validation tag.
    "},{"location":"getting-started/features/encrypted-mnemonics/#considerations","title":"Considerations","text":"

    Storage of encrypted mnemonics on the device or SD cards are meant for convenience only and should not be considered a form of backup. Always make a physical backup of your keys that is independent from electronic devices and test recovering your wallet from this backup before you send funds to it.

    Remember that the stored encrypted mnemonic is protected by the key you defined to encrypt it. If the defined key is weak, your encrypted mnemonic will not be protected. If you have stored a mnemonic with funds in the device's internal flash memory using a weak key, the best way to undo this is to wipe the device.

    "},{"location":"getting-started/features/printing/","title":"Printing","text":"

    Krux has the ability to print all QR codes it generates, including mnemonic, xpub, wallet backup, and signed PSBT, via a locally-connected thermal printer over its serial port. Consult the part list page for supported printers.

    Once connected and powered on, all screens that display a QR code will begin showing a follow-up screen asking if you want to Print to QR?.

    There are many ways you can use this functionality, including:

    • Printing backups of your mnemonics and multisig wallets
    • Printing your xpubs and receive addresses to share
    • Printing signed messages and PSBTs

    Since printed thermal paper fades quickly, you can also print your backups on sticker thermal paper to use as templates for punching into more resilient materials like steel.

    We also have plans to add support for other kinds of QR \"printers\" in the future, including CNC machines. In this case, gcode will be generated that can be sent directly to a GRBL controller to cut your QRs out of wood or metal!

    Just be careful what you do with the printed codes, since most smartphones can now quickly and easily read QR codes. Treat your QR mnemonic the same way you would treat a plaintext copy of it.

    "},{"location":"getting-started/features/sd-card-update/","title":"SD Card Updates","text":""},{"location":"getting-started/features/sd-card-update/#upgrade-via-microsd-card","title":"Upgrade via microSD card","text":"

    Once you've installed the initial firmware on your device via USB, you can either continue updating the device by flashing via USB or you can perform upgrades via microSD card to keep the device airgapped.

    To perform an upgrade, simply copy the official release firmware.bin and firmware.bin.sig files to the root of a FAT-32 / MBR formatted microSD card, insert the card into your device, and reboot the device. If it detects the new firmware file and is able to verify the signature, you will be prompted to install it.

    Once installation is complete, eject the microSD card and delete the firmware files before reinserting and rebooting. Otherwise you will be prompted to install it again.

    We cannot guarantee that a microSD card is compatible and will work in your device; you'll need to test it on the device to be sure, read the FAQ for more info. Only official releases are signed and can be installed via microSD card.

    "},{"location":"getting-started/features/tools/","title":"Tools","text":"

    Here are some useful tools that are available as soon as Krux starts! These are offered as a complement to managing your device and wallets.

    "},{"location":"getting-started/features/tools/#check-sd-card","title":"Check SD Card","text":"

    You can check if a SD card can be detected and read by your device and explore its content.

    "},{"location":"getting-started/features/tools/#print-test-qr","title":"Print Test QR","text":"

    Quickly print a test QR code to check and optimize your printer setup.

    "},{"location":"getting-started/features/tools/#create-qr-code","title":"Create QR Code","text":"

    Enter a text input to create, print or transcript a QR code that can be later used as an encryption key or as a passphrase. Swipe left or right to change modes if your device has a touchscreen.

    "},{"location":"getting-started/features/tools/#remove-mnemonic","title":"Remove Mnemonic","text":"

    This option allows you to remove any stored encrypted mnemonic from the device's internal memory or an SD card. For more information, see Krux Encrypted Mnemonics.

    When mnemonics are removed from the device's flash memory, Krux will no longer be able to access them. However, as with most operating systems, the data may still be recoverable using specialized tools. If you stored any important keys with a weak encryption key, it is recommended to use the \"Wipe Device\" feature below to ensure that the data is irrecoverable.

    When mnemonics are removed from an SD card, Krux will overwrite the region where the encrypted mnemonic was stored with empty data. This makes it more secure to delete mnemonics from SD cards using Krux rather than a PC or another device. However, Krux does not have a \"Wipe\" feature for SD cards; you can find this feature in third-party applications.

    "},{"location":"getting-started/features/tools/#wipe-device","title":"Wipe Device","text":"

    This option permanently removes all stored encrypted mnemonics and settings from the device's internal flash memory. It ensures that the data is irrecoverable, making it an adequate measure to take if any important mnemonics were stored with a weak encryption key.

    "},{"location":"getting-started/installing/from-gui/","title":"From GUI application","text":"

    You can install Krux (both official or beta releases) onto your K210-based device using our official desktop application, KruxInstaller, available for Linux and Windows.

    "},{"location":"getting-started/installing/from-gui/#download","title":"Download","text":"

    Download the installer by choosing the right asset for your operating system from our Github releases page:

    Operational System File Windows krux-installer_0.0.13.exe* Debian-based: Ubuntu, PopOS, etc... krux-installer_0.0.13_amd64.deb* RedHat-based: Fedora, etc... krux-installer-0.0.13.x86_64.rpm* Any linux distribution krux-installer-0.0.13.AppImage* Package for Archlinux on AUR. krux-installer-bin"},{"location":"getting-started/installing/from-gui/#verify-files","title":"Verify files","text":"

    If you trust the project developers, you can skip to install:

    \u26a0\ufe0f TIP: This step helps detect if any unauthorized modifications were made between github and your local computer.

    "},{"location":"getting-started/installing/from-gui/#integrity","title":"Integrity","text":"System Commands Windows (powershell)
    # Compare this output:\n(Get-FileHash 'krux-installer_0.0.13.exe').Hash\n\n# With this:\nGet-Content 'krux-installer_0.0.13.exe.sha256.txt'\n
    Debian-based
    sha256sum --check ./krux-installer_0.0.13_amd64.deb.sha256.txt\n
    RedHat-based
    sha256txt --check ./krux-installer-0.0.13.x86_64.rpm.sha256.txt\n
    Any Linux distribution
    sha256sum --check ./krux-installer-0.0.13.AppImage.sha256.txt\n
    "},{"location":"getting-started/installing/from-gui/#authenticity","title":"Authenticity","text":"

    You will need have GPG installed. Most Linux already have this, but on Windows we recommend installing GPG4Win.

    Once installed, run this command to retrieve the developer's key:

    gpg --keyserver hkps://keys.openpgp.org --recv-keys B4281DDDFBBD207BFA4113138974C90299326322\n

    Then you can verify:

    System Command Windows (powershell) gpg --verify krux-installer_0.0.13.exe.sig Debian-based gpg --verify ./krux-installer_0.0.13_amd64.deb.sig RedHat-based gpg --verify ./krux-installer-0.0.13.x86_64.rpm.sig Any Linux distribution gpg --verify ./krux-installer-0.0.13.AppImage.sig

    \u26a0\ufe0f TIP: If the verification was successful, you may get a message similar to: Good signature from \"qlrddev <qlrddev@gmail.com>\"

    "},{"location":"getting-started/installing/from-gui/#install","title":"Install","text":"

    Each system require different steps to install:

    System Steps Windows
    • The krux-installer_0.0.13.exe is a NSIS installer;
    • The first time you run the .exe file the system will ask you to trust the application;
    • See windows section below for more information
    • Debian-based
      • Install with dpkg: sudo dpkg -i krux-installer_0.0.13_amd64.deb;
      • Update it with apt-get: sudo apt-get install -f krux-installer_0.0.13_amd64.deb.
      RedHat-based
      • Fedora: sudo dnf install krux-installer-0.0.13.x86_64.rpm;
      • Other RedHat based distros: sudo yum localinstall krux-installer-0.0.13.x86_64.rpm.
      Any Linux distribution
      • Place the krux-installer-0.0.13.AppImage where you want;
      • Modify permision to execute: chmod +x krux-installer-0.0.13.AppImage;
      • Run it: ./krux-installer-0.0.13.AppImage.
      "},{"location":"getting-started/installing/from-gui/#windows","title":"Windows","text":"

      On Windows, you'll be faced with a blue window saying \"Windows protected your PC\". This occurs because we don't have a code signing certificate, Click on \"More info\" and allow the execution. Follow the installer's instructions to complete the installation.

      \ud83d\udee1\ufe0f TIP: If you followed the steps presented in authenticity, you already have the assurance that the software is from a verified and genuine software publisher

      "},{"location":"getting-started/installing/from-gui/#usage","title":"Usage","text":""},{"location":"getting-started/installing/from-gui/#main-menu","title":"Main Menu","text":"

      When executing the Krux Installer, you will be presented with a menu of two items:

      "},{"location":"getting-started/installing/from-gui/#select-device","title":"Select Device","text":"

      First, select the device we want to flash. After that the menu will shown three items:

      "},{"location":"getting-started/installing/from-gui/#select-version","title":"Select Version","text":"

      This option allows you to select the latest official version or the latest beta version on our github. If the connection works, you will see the following screen:

      Version Name Advantages Disadvantages Official selfcustody/krux/releases/tag/*
      • Stable version;
      • All features were reviewd and tested;
      • It's integrity and authenticity can be verified.
        • Updates may take some time to be released.
        Beta odudex/krux_binaries
        • Latest features available;
        • Constant UX reviews by our community.
        • Its integrity or authenticity cannot be verified.
        "},{"location":"getting-started/installing/from-gui/#flash","title":"Flash","text":"

        Once you choose the device and version, it enables the \"flash with ktool\" button.

        \u26a0\ufe0f TIP: You must connect and turn on your device before flashing. In Linux, you'll be prompted to type your password (sudo or admin). Do not disconnect the device until the process is complete!

        When the flashing process completes, you should see the Krux logo:

        If it doesn't, try turning your device off and on by holding down the power button for six seconds.

        Congrats, you're now running Krux!

        "},{"location":"getting-started/installing/from-gui/#wipe-device","title":"Wipe device","text":"

        This option erases ALL data from the device's flash memory. On Linux, it will prompt for your password before. Once the process starts, the screen will appear frozen. When it's done, you can scroll down and see all the events that occurred.

        \u26a0\ufe0f TIP: It's useful when your device is not working or for security reasons. To use Krux again, you'll need to re-flash the firmware.

        \u26a0\ufe0f TIP: Do not unplug or poweroff your device or computer. Wait until the process finishes.

        "},{"location":"getting-started/installing/from-gui/#multilingual-support","title":"Multilingual support","text":"

        Prefer a different language? Krux has support for multiple languages. Once at the start screen, go to Settings, followed by Locale, and select the locale you wish to use.

        "},{"location":"getting-started/installing/from-gui/#upgrade-via-microsd-card","title":"Upgrade via microSD card","text":"

        Once you've installed the initial firmware on your device via USB, you can either continue updating the device by flashing or you can perform upgrades via microSD card to keep the device airgapped.

        "},{"location":"getting-started/installing/from-pre-built-release/","title":"From pre-built official release","text":"

        This page explains how to install Krux from an official, pre-built release.

        "},{"location":"getting-started/installing/from-pre-built-release/#download-the-latest-release","title":"Download the latest release","text":"

        Head over to the releases page and download the latest signed release.

        "},{"location":"getting-started/installing/from-pre-built-release/#verify-the-files","title":"Verify the files","text":"

        Before installing the release, it's a good idea to check that:

        1. The SHA256 hash of krux-vX.Y.Z.zip matches the hash in krux-vX.Y.Z.zip.sha256.txt
        2. The signature file krux-vX.Y.Z.zip.sig can be verified with the selfcustody.pem public key found in the root of the krux repository.

        You can either do this manually or with the krux shell script, which contains helper commands for this:

        ./krux sha256 krux-vX.Y.Z.zip\n./krux verify krux-vX.Y.Z.zip selfcustody.pem\n

        Fun fact: Each Krux release is signed with Krux!

        "},{"location":"getting-started/installing/from-pre-built-release/#flash-the-firmware-onto-the-device","title":"Flash the firmware onto the device","text":"

        Extract the latest version of Krux you downloaded and enter the folder:

        unzip krux-vX.Y.Z.zip && cd krux-vX.Y.Z\n

        Connect the device to your computer via USB (for Maix Amigo, make sure you\u2019re using bottom port), power it on, and run the following, replacing DEVICE with either m5stickv, amigo, bit or yahboom (to yahboom you may need to manually specify the port):

        ./ktool -B goE -b 1500000 maixpy_DEVICE/kboot.kfpkg\n

        For dock the -B parameter changes, so run:

        ./ktool -B dan -b 1500000 maixpy_dock/kboot.kfpkg\n

        When the flashing process completes, you should see the Krux logo:

        If it doesn't, try turning your device off and on by holding down the power button for six seconds.

        Congrats, you're now running Krux!

        "},{"location":"getting-started/installing/from-pre-built-release/#a-note-about-the-maix-amigo","title":"A note about the Maix Amigo","text":"

        Some Amigo screens have inverted X coordinates, others display colors differently. For more info see FAQ

        "},{"location":"getting-started/installing/from-pre-built-release/#troubleshooting","title":"Troubleshooting","text":"

        If ktool fails to run, you may need to give it executable permissions with chmod +x ./ktool, or you might need to use \"sudo\" if your user don't have access to serial port. In Windows or Mac you may need to explicitly allow the tool to run by adding an exception for it.

        If the flashing process fails midway through, check the connection, restart the device, and try the command again.

        Two serial ports are created when Amigo and Bit are connected to a PC. Sometimes Ktool will pick the wrong and flash will fail. Manually specify the serial port to overcome this issue using -p argument:

        ./ktool-linux -B goE -b 1500000 maixpy_amigo/kboot.kfpkg -p /dev/ttyUSB1\n

        Check por names of devices manager on Windows (e.g. COM1, COM9), or list the ports on linux

        ls /dev/ttyUSB*\n

        List ports on Mac

        ls /dev/cu.usbserial*\n
        Different OS versions may have different port names, and the absence of ports may indicate a connection, driver or hardware related issue.

        "},{"location":"getting-started/installing/from-pre-built-release/#multilingual-support","title":"Multilingual support","text":"

        Prefer a different language? Krux has support for multiple languages. Once at the start screen, go to Settings, followed by Locale, and select the locale you wish to use.

        "},{"location":"getting-started/installing/from-pre-built-release/#upgrade-via-microsd-card","title":"Upgrade via microSD card","text":"

        Once you've installed the initial firmware on your device via USB, you can either continue updating the device by flashing or you can perform upgrades via microSD card to keep the device airgapped.

        "},{"location":"getting-started/installing/from-source/","title":"From source","text":"

        This page explains how to install Krux from source. You can check these instructions in our README too.

        "},{"location":"getting-started/installing/from-source/#software","title":"Software","text":"

        You will need a computer with git and vagrant installed.

        "},{"location":"getting-started/installing/from-source/#fetch-the-code","title":"Fetch the code","text":"

        In a terminal, run the following:

        git clone --recurse-submodules https://github.com/selfcustody/krux\n
        This will pull down the Krux source code as well as the code for all its dependencies and put them inside a new krux folder.

        Note: When you wish to pull down updates to this repo, you can run the following inside the krux folder:

        git pull origin main && git submodule update --init --recursive\n

        "},{"location":"getting-started/installing/from-source/#spin-up-a-virtual-machine","title":"Spin up a virtual machine","text":"

        After you have installed Vagrant, run the following inside the krux folder to spin up a new VM:

        vagrant up\n

        "},{"location":"getting-started/installing/from-source/#build-the-firmware","title":"Build the firmware","text":""},{"location":"getting-started/installing/from-source/#prerequisite-for-upgrading-via-microsd","title":"Prerequisite for upgrading via microSD","text":"

        If you wish to perform airgapped upgrades via microSD card, you will need to have a private and public key pair to sign your builds and verify the signatures.

        You can use an existing Krux installation and mnemonic to sign your builds with, or you can generate a keypair and sign from the openssl CLI. Commands have been added to the krux shell script to make this easier.

        In either case, you will need to update the SIGNER_PUBKEY field in src/krux/metadata.py to store your public key so that Krux can verify future builds before installing.

        To generate a keypair, run:

        vagrant ssh -c 'cd /vagrant; ./krux generate-keypair'\nvagrant ssh -c 'cd /vagrant; ./krux pem-to-pubkey pubkey.pem'\n

        The first command will create privkey.pem and pubkey.pem files you can use with openssl, and the second command will output your public key in the form expected by Krux.

        Once you've updated the SIGNER_PUBKEY with this value, you can proceed with the regular build process.

        "},{"location":"getting-started/installing/from-source/#build","title":"Build","text":"

        Run the following, replacing DEVICE with either m5stickv, amigo, dock, bit or yahboom:

        vagrant ssh -c 'cd /vagrant; ./krux build maixpy_DEVICE'\n

        This will take around an hour or so to complete the first time. Subsequent builds should take only a few minutes.

        If all goes well, you should see a new build folder containing firmware.bin and kboot.kfpkg files when the build completes.

        "},{"location":"getting-started/installing/from-source/#reproducibility","title":"Reproducibility","text":"

        If you build from the main branch of the source code, you should be able to reproduce the build process used to generate the last release binaries and obtain exact copies of the firmware.bin and kboot.kfpkg files, with matching hash checksums.

        To extract and verify the firmware.bin contained in kboot.kfpkg, you can use the following command:

        unzip kboot.kfpkg -d ./kboot/

        "},{"location":"getting-started/installing/from-source/#flash-the-firmware-onto-the-device","title":"Flash the firmware onto the device","text":"

        Connect the device to your computer via USB (for Maix Amigo, make sure you\u2019re using bottom port), power it on, and run the following, replacing DEVICE with either m5stickv, amigo, dock, bit or yahboom:

        vagrant ssh -c 'cd /vagrant; ./krux flash maixpy_DEVICE'\n
        If the flashing fails try one of the following common solutions:

        • Running vagrant reload prior to flashing in order for the newly-inserted USB device to be detected and passed through to the VM on startup.
        • If this command fails, even after reloading, with the error Failed to find device via USB. Is it connected and powered on?, make sure that your user has been added to the vboxusers group. On Mac or Linux, run the following command:
          sudo usermod -a -G vboxusers yourusername\n
        • If the flashing process fails midway through, check the connection, restart the device, and try the command again.

        When the flashing process completes, you should see the Krux logo:

        If it doesn't, try turning your device off and on by holding down the power button for six seconds.

        Congrats, you're now running Krux!

        "},{"location":"getting-started/installing/from-source/#a-note-about-the-maix-amigo","title":"A note about the Maix Amigo","text":"

        Some Amigo screens have inverted X coordinates, others display colors differently. For more info see FAQ

        "},{"location":"getting-started/installing/from-source/#signing-the-firmware","title":"Signing the firmware","text":"

        You can sign the firmware using one of the two methods listed below:

        "},{"location":"getting-started/installing/from-source/#method-1-signing-from-krux","title":"Method 1: Signing from Krux","text":"

        First, calculate the SHA256 hash of the new firmware by running:

        vagrant ssh -c 'cd /vagrant; ./krux sha256 build/firmware.bin'\n

        Copy this hex string and turn it into a QR code using whichever QR code generator you'd like.

        In Krux, enter the mnemonic of your private key that will be used for signing, and go to Sign > Message. Scan the QR code you generated, and you will be asked if you wish to sign the hash. Proceed, and you will be presented with a base64-encoded string containing the signature, as text and as a QR code.

        Take this string and create a signature file by running:

        vagrant ssh -c 'cd /vagrant; ./krux b64decode \"signature-in-base64\" > build/firmware.bin.sig'\n

        This will generate a firmware.bin.sig file containing a signature of the firmware's SHA256 hash.

        "},{"location":"getting-started/installing/from-source/#method-2-signing-from-your-computer-with-openssl","title":"Method 2: Signing from your computer with OpenSSL","text":"

        With the keypair you generated before, you can now run:

        vagrant ssh -c 'cd /vagrant; ./krux sign build/firmware.bin privkey.pem'\n

        This will generate a firmware.bin.sig file containing a signature of the firmware's SHA256 hash.

        "},{"location":"getting-started/installing/from-test-release/","title":"From pre-built test release","text":"

        This page explains how to install Krux from a test (beta), pre-built release.

        "},{"location":"getting-started/installing/from-test-release/#warning","title":"Warning","text":"

        Keep in mind that these are unsigned binaries.

        "},{"location":"getting-started/installing/from-test-release/#download","title":"Download","text":"

        Download experimental compiled firmware or the Android app apk: Krux binaries

        "},{"location":"getting-started/installing/from-test-release/#android","title":"Android","text":"

        Krux Android app is intended for learning about Krux and Bitcoin airgapped transactions. Vulnerabilities inherent to Android phones such as the OS, other apps and wireless / CDMA / GSM / Bluetooth connectivity make using any phone insecure. Krux app should NOT be used to manage savings or important keys and mnemonics. For that, a dedicated device is recommended.

        "},{"location":"getting-started/installing/from-test-release/#m5stickv","title":"M5StickV","text":""},{"location":"getting-started/installing/from-test-release/#linux","title":"Linux","text":"

        To Flash M5stickV run:

        ./ktool-linux -B goE -b 1500000 maixpy_m5stickv/kboot.kfpkg\n

        "},{"location":"getting-started/installing/from-test-release/#windows","title":"Windows","text":"

        Replace './ktool-linux' for 'ktool-win.exe' and / for \\ in commands:

        ./ktool-win.exe -B goE -b 1500000 maixpy_m5stickv\\kboot.kfpkg\n

        "},{"location":"getting-started/installing/from-test-release/#sipeed-maix-amigo","title":"Sipeed Maix Amigo","text":""},{"location":"getting-started/installing/from-test-release/#linux_1","title":"Linux","text":"

        To Flash Maix Amigo run:

        ./ktool-linux -B goE -b 1500000 maixpy_amigo/kboot.kfpkg\n

        "},{"location":"getting-started/installing/from-test-release/#windows_1","title":"Windows","text":"

        Replace './ktool-linux' for 'ktool-win.exe' and / for \\ in commands:

        ./ktool-win.exe -B goE -b 1500000 maixpy_amigo\\kboot.kfpkg\n

        "},{"location":"getting-started/installing/from-test-release/#a-note-about-the-maix-amigo","title":"A note about the Maix Amigo","text":"

        Some Amigo screens have inverted X coordinates, others display colors differently. For more info see FAQ

        "},{"location":"getting-started/installing/from-test-release/#sipeed-maix-bit","title":"Sipeed Maix Bit","text":""},{"location":"getting-started/installing/from-test-release/#linux_2","title":"Linux","text":"

        To Flash Maix Bit run:

        ./ktool-linux -B goE -b 1500000 maixpy_bit/kboot.kfpkg\n

        "},{"location":"getting-started/installing/from-test-release/#windows_2","title":"Windows","text":"

        Replace './ktool-linux' for 'ktool-win.exe' and / for \\ in commands:

        ./ktool-win.exe -B goE -b 1500000 maixpy_bit\\kboot.kfpkg\n

        "},{"location":"getting-started/installing/from-test-release/#sipeed-maix-dock","title":"Sipeed Maix Dock","text":""},{"location":"getting-started/installing/from-test-release/#linux_3","title":"Linux","text":"

        To Flash Maix Dock run:

        ./ktool-linux -B dan -b 1500000 maixpy_dock/kboot.kfpkg\n

        "},{"location":"getting-started/installing/from-test-release/#windows_3","title":"Windows","text":"

        Replace './ktool-linux' for 'ktool-win.exe' and / for \\ in commands:

        ./ktool-win.exe -B dan -b 1500000 maixpy_dock\\kboot.kfpkg\n

        "},{"location":"getting-started/installing/from-test-release/#aimotion-yahboom-k210-module","title":"Aimotion Yahboom k210 module","text":""},{"location":"getting-started/installing/from-test-release/#linux_4","title":"Linux","text":"

        To Flash Yahboom k210 module you'll have to manually specify the port, on this example /dev/ttyUSB0:

        ./ktool-linux -B goE -b 1500000 -p /dev/ttyUSB0 yahboom/kboot.kfpkg\n

        "},{"location":"getting-started/installing/","title":"Installing","text":"

        You can install Krux in four different ways:

        • From GUI application
        • From pre-built official release
        • From pre-built test (beta) release
        • From source
        "},{"location":"getting-started/installing/#requirements","title":"Requirements","text":"

        Please, check the part list for the compatible devices and requirements.

        After the first firmware install, you can use a microSD card if you wish to perform further airgapped updates.

        "},{"location":"getting-started/usage/generating-a-mnemonic/","title":"Generating a Mnemonic","text":"

        Krux has support for creating 12 and 24-word mnemonic seed phrases. Since true entropy is difficult to produce, especially with an embedded device, we recommend to outsource entropy generation using dice rolls, but it is also possible to use camera as a source of entropy to quickly create a mnemonic. At the start screen, once you select New Mnemonic, you will be taken to a second menu where you can choose to create a mnemonic via camera, via rolls of a D6 (standard six-sided die) or D20 (20-sided die).

        "},{"location":"getting-started/usage/generating-a-mnemonic/#camera","title":"Camera","text":"

        (Experimental!) Choose between 12 or 24 words, then take a random picture and Krux will generate a mnemonic from the hash of the image bytes.

        "},{"location":"getting-started/usage/generating-a-mnemonic/#image-entropy-quality-estimation","title":"Image Entropy Quality Estimation","text":"

        During image capture, entropy quality estimation is displayed to assist you in obtaining a high-quality image source for your key. After a snapshot is taken, Shannon's entropy and pixel deviation indices are presented. Minimum thresholds are established to prevent the use of poor-quality images with low entropy for key generation. It's important to note that these values serve as indicators or estimations of entropy quality, but they are not absolute entropy values in a cryptographic context.

        "},{"location":"getting-started/usage/generating-a-mnemonic/#dice-rolls","title":"Dice Rolls","text":""},{"location":"getting-started/usage/generating-a-mnemonic/#via-d6","title":"Via D6","text":"

        Choose between 12 or 24 words.

        The entropy in a single roll of a D6 is 2.585 bits ( log2(6) ); therefore a minimum of a 50 rolls will be required for 128 bits of entropy, enough to generate a 12-word mnemonic. For 24 words, or an entropy of 256 bits, a minimum of 99 rolls will be required.

        "},{"location":"getting-started/usage/generating-a-mnemonic/#via-d20","title":"Via D20","text":"

        Since a D20 has more possible outcomes, the entropy is increased per roll to 4.322 bits ( log2(20) ). This means that only 30 rolls are necessary to create a 12-word mnemonic and 60 rolls for a 24-word mnemonic.

        "},{"location":"getting-started/usage/generating-a-mnemonic/#dice-rolls-entropy-quality-estimation","title":"Dice Rolls Entropy Quality Estimation","text":"

        When you input your dice rolls, you'll see two progress bars filling up. The top progress bar shows how many rolls you've entered compared to the minimum number needed. The bottom progress bar shows the real-time calculated Shannon's entropy compared to the required minimum (128 bits for 12 words and 256 bits for 24 words). When the Shannon's entropy estimation reaches the recommended level, the progress bar will be full, and its frame will change color. If you've met the minimum number of rolls but the entropy estimation is still below the recommended level, a warning will appear, suggesting you add more rolls to increase entropy. Note: Similar to image entropy quality estimation, dice rolls Shannon's entropy serves as an indicator and should not be considered an absolute measure of cryptographic entropy.

        "},{"location":"getting-started/usage/generating-a-mnemonic/#stats-for-nerds","title":"Stats for Nerds","text":"

        A low Shannon's entropy value might suggest that your dice are biased or that there's a problem with how you're gathering entropy. To investigate further, examine the \"Stats for Nerds\" section to check the distribution of your rolls and look for any abnormalities.

        "},{"location":"getting-started/usage/generating-a-mnemonic/#how-it-works","title":"How it works","text":"

        For dice rolls, Krux keeps track of every roll you enter and displays the cumulative string of outcomes after each roll.

        When you have entered your final roll, Krux will hash this string using SHA256 and output the resulting hash to the screen so that you can verify it for yourself.

        In the case a camera snapshot is used as source, image bytes, which contain pixels data in RGB565 format, will be hashed just like it is done with the dice rolls string.

        Krux then takes this hash, runs unhexlify on it to encode it as bytes, and deterministically converts it into a mnemonic according to the BIP-39 Reference Implementation.

        Note: For 12-word mnemonics, only the first half of the SHA256 hash is used (128 bits), while 24-word mnemonics use the full hash (256 bits).

        "},{"location":"getting-started/usage/generating-a-mnemonic/#how-to-verify","title":"How to verify","text":"

        Don't trust, verify. We encourage you not to trust any claim you cannot verify yourself. Therefore, there are wallets that use compatible algorithms to calculate the entropy derived from dice rolls. You can use the SeedSigner or Coldcard hardware wallets, or even the Bitcoiner Guide website, they share the same logic that Krux uses and will give the same mnemonic for the dice roll method.

        "},{"location":"getting-started/usage/generating-a-mnemonic/#alternatives","title":"Alternatives","text":"

        You can use any other offline airgapped devices to generate your mnemonic. If you have an old Android smartphone that is offline (in airplane mode [no active CDMA or GSM chip], no Wifi connection, no Bluetooth and localization service turned off), you can use the Krux app for Android. If you want to use a regular PC, a common strategy is to boot the PC using Tails from a USB stick, without connecting the device to the internet, and then you can run Krux using our simulator, use a copy of the the Bitcoiner Guide website or even Ian Coleman's BIP-39 Tool. It's worth noting that both generate a QR code that Krux can read via the QR input method mentioned on the next page (Loading a Mnemonic).

        "},{"location":"getting-started/usage/loading-a-mnemonic/","title":"Loading a Mnemonic","text":"

        Once you have either a 12- or 24-word mnemonic, choose Load Mnemonic on Krux's start menu, and you will be presented with several input methods:

        "},{"location":"getting-started/usage/loading-a-mnemonic/#input-methods","title":"Input Methods","text":""},{"location":"getting-started/usage/loading-a-mnemonic/#via-camera","title":"Via Camera","text":"

        You can choose to use the camera to scan a QR code or Tiny Seed metal plate backup.

        "},{"location":"getting-started/usage/loading-a-mnemonic/#qr-code","title":"QR Code","text":"

        It's unpleasant having to manually enter 12 or 24 words every time you want to use Krux. To remedy this you can instead use the device's camera to read a QR code containing the words. Krux will decode QR codes of four types:

        1. Plain text QR: The mnemonic words encoded as text, with words separated by spaces.
        2. SeedQR: Basically, it is the mnemonic words of the respective BIP-39 numbers concatenated, encoded as text.
        3. Compact SeedQR: Basically, it is the mnemonic words bits concatenated as bytes.
        4. Encrypted Mnemonic: A specification created by Krux that encrypts the mnemonic words bits and adds some information about the encryption used.

        After opening your wallet via one of the manual methods you can use Krux to create QR codes of all types above, transcript them to paper or metal using the transcription helpers or attach a thermal printer to your Krux and print out the mnemonic. Check out the Printing section for more information. You can also use an offline QR code generator for this (ideally on an airgapped device).

        "},{"location":"getting-started/usage/loading-a-mnemonic/#tiny-seed","title":"Tiny Seed","text":"

        Tiny Seed is a compact metal plate mnemonic backup method. Krux devices have machine vision capabilities that allow users to scan these metal plates and instantly load mnemonics engraved on them. To properly scan them place the Tiny Seed over a black background and paint the punched bits black to increase contrast. You can also scan the thermally printed version, or a filled template. Find templates to scan or print here.

        "},{"location":"getting-started/usage/loading-a-mnemonic/#via-manual-input","title":"Via Manual Input","text":"

        Manually type Words, Word Numbers, Tiny Seed (toggle the bits or punches) or Stackbit (model 1248 metal plate backup).

        "},{"location":"getting-started/usage/loading-a-mnemonic/#words","title":"Words","text":"

        Enter each word of your BIP-39 mnemonic one at a time. Krux will disable impossible-to-reach letters as you type and will attempt to autocomplete your words to speed up the process.

        On your 12th or 24th word, you can leave it blank to have Krux generate the final checksum word of your mnemonic for you. This is useful when you choose a manual method to generate your mnemonic and want the final BIP-39 checksum word to be valid.

        "},{"location":"getting-started/usage/loading-a-mnemonic/#word-numbers","title":"Word Numbers","text":""},{"location":"getting-started/usage/loading-a-mnemonic/#decimal","title":"Decimal","text":"

        Enter each word of your BIP-39 mnemonic as a number from 1 to 2048 one at a time. You can use this list for reference.

        "},{"location":"getting-started/usage/loading-a-mnemonic/#hexadecimal-and-octal","title":"Hexadecimal and Octal","text":"

        You can also enter your BIP-39 mnemonic word's numbers (1-2048) in hexadecimal format, with values ranging from 0x1 to 0x800, or in octal format, with values ranging from 01 to 04000. This is useful with some metal plate backups that uses those formats.

        "},{"location":"getting-started/usage/loading-a-mnemonic/#final-checksum-word","title":"Final checksum word","text":"

        On your 12th or 24th word, you can leave it blank to have Krux generate the final checksum word of your mnemonic for you. This is useful when you choose a manual method to generate your mnemonic and want the final BIP-39 checksum word to be valid.

        "},{"location":"getting-started/usage/loading-a-mnemonic/#tiny-seed-bits","title":"Tiny Seed (Bits)","text":"

        Enter the BIP-39 mnemonic word's numbers (1-2048) in binary format, toggling necessary bits to recreate each of the word's respective number. The last word will have checksum bits dynamically toggled while you fill the bits.

        "},{"location":"getting-started/usage/loading-a-mnemonic/#stackbit-1248","title":"Stackbit 1248","text":"

        Enter the BIP-39 mnemonic word's numbers (1-2048) using the Stackbit 1248 metal plate backup method, where each of the four digits of the word's number is a sum of the numbers marked (punched) 1, 2, 4, or 8. For example, to enter the word \"pear\", number 1297, you must punch (1)(2)(1+8=9)(1+2+4=7).

        "},{"location":"getting-started/usage/loading-a-mnemonic/#from-storage","title":"From Storage","text":"

        You can retrieve mnemonics previously stored on device's internal flash or external (SD card). All stored mnemonics are encrypted, to load them you'll have to enter the same key you used to encrypt them.

        "},{"location":"getting-started/usage/loading-a-mnemonic/#wallet-loading-sequence","title":"Wallet loading sequence","text":""},{"location":"getting-started/usage/loading-a-mnemonic/#confirm-mnemonic-words","title":"Confirm Mnemonic Words","text":"

        Once you have entered your mnemonic, you will be presented with the full list of words to confirm.

        "},{"location":"getting-started/usage/loading-a-mnemonic/#passphrase","title":"Passphrase","text":"

        After confirming the mnemonic words, you can optionally choose to type or scan a BIP-39 passphrase. When typing, swipe left or right to change modes if your device has a touchscreen. For scanning, you can also create a QR code from your offline passphrase in Tools.

        "},{"location":"getting-started/usage/loading-a-mnemonic/#fingerprint","title":"Fingerprint","text":"

        The wallet's fingerprint, if you have it noted down, will help you make sure you entered the correct mnemonic and passphrase (optional) and will load the expected wallet.

        "},{"location":"getting-started/usage/loading-a-mnemonic/#single-sig-or-multisig","title":"Single-sig or Multisig","text":"

        After loading your mnemonic and passphrase (optional), you will be asked if you want to use it as part of a Single-sig or Multisig wallet.

        Your choice here will subtly change the generated xpub that is used to set up your device in your wallet coordinator software. You can learn more about the difference in the following guides for using single-sig and multisig wallets.

        Now, onto the main menu...

        "},{"location":"getting-started/usage/navigating-the-main-menu/","title":"Navigating the Main Menu","text":"

        After entering your mnemonic, and loading a wallet, you will find yourself on Krux's main menu. Below is a breakdown of the entries available:

        "},{"location":"getting-started/usage/navigating-the-main-menu/#mnemonic","title":"Mnemonic","text":"

        This will open a new submenu with the following options to backup your mnemonic. If you set a printer driver, it will also give the option to print the backup!

        "},{"location":"getting-started/usage/navigating-the-main-menu/#words","title":"Words","text":"

        Display the mnemonic words as text so you can write them down.

        "},{"location":"getting-started/usage/navigating-the-main-menu/#numbers","title":"Numbers","text":"

        Display the mnemonic word numbers in decimal, hex, or octal format.

        "},{"location":"getting-started/usage/navigating-the-main-menu/#plaintext-qr","title":"Plaintext QR","text":"

        Generate a QR containing the mnemonic words as regular text, where words are separated by spaces.

        "},{"location":"getting-started/usage/navigating-the-main-menu/#compact-seedqr","title":"Compact SeedQR","text":"

        A QR code is created from a binary representation of mnemonic words. Format created by SeedSigner backup-qr-compact

        "},{"location":"getting-started/usage/navigating-the-main-menu/#seedqr","title":"SeedQR","text":"

        Words are converted to their BIP-39 numeric indexes, those numbers are then concatenated as a string and finally converted to a QR code. Format created by SeedSigner

        "},{"location":"getting-started/usage/navigating-the-main-menu/#stackbit-1248","title":"Stackbit 1248","text":"

        This metal backup format represents the BIP-39 mnemonic word's numbers (1-2048). Each of the four digits is converted to a sum of 1, 2, 4 or 8. This option does not print even if a printer driver is set.

        "},{"location":"getting-started/usage/navigating-the-main-menu/#tiny-seed","title":"Tiny Seed","text":"

        This metal backup format represents the BIP-39 mnemonic word's numbers (1-2048) in binary format on a metal plate, where the 1's are marked (punched) and the 0's are left intact.

        "},{"location":"getting-started/usage/navigating-the-main-menu/#encrypt-mnemonic","title":"Encrypt Mnemonic","text":"

        This feature allows you to back up your mnemonic by encrypting it and storing it on the device's flash memory, an SD card, or in QR code format. You can customize the encryption method and parameters in the settings.

        For convenience, you may choose to store the encrypted mnemonic on flash memory or an SD card, but it is advisable not to rely solely on these methods for backup. Flash storage can degrade over time and may be subject to permanent damage, resulting in the loss of stored information.

        When using any of the encryption methods, you will be prompted to enter an encryption key. This key can be provided in text or QR code format. Additionally, you have the option to set a custom ID for easier management of your mnemonics. If a custom key is not specified, the device's current loaded wallet fingerprint will be used as the ID.

        "},{"location":"getting-started/usage/navigating-the-main-menu/#store-on-flash","title":"Store on Flash","text":"

        This option stores the encrypted mnemonic in the device's flash memory. You can decrypt and load it later through the \"Load Mnemonic\" -> \"From Storage\" option.

        "},{"location":"getting-started/usage/navigating-the-main-menu/#store-on-sd-card","title":"Store on SD Card","text":"

        If an SD card is available, this option stores the encrypted mnemonic on it. You can decrypt and load it later through the \"Load Mnemonic\" -> \"From Storage\" option.

        "},{"location":"getting-started/usage/navigating-the-main-menu/#encrypted-qr-code","title":"Encrypted QR Code","text":"

        This option converts the encrypted mnemonic into a QR code. When you scan this QR code through \"Load Mnemonic\" -> \"Via Camera\" -> \"QR Code,\" you will be prompted to enter the decryption key to load the mnemonic stored in it.

        Learn more about Krux Mnemonics Encryption

        "},{"location":"getting-started/usage/navigating-the-main-menu/#extended-public-key","title":"Extended Public Key","text":"

        A menu will be presented with options to display your master extended public key (xPub) as text and as a QR code. Depending on whether a single-sig or multisig wallet was loaded, the options shown will be xPub, zPub, or ZPub. When displayed as text, the extended public key can be stored on an SD card if available. If you choose to export a QR code, you can not only scan it but also save it as an image on an SD card or print it if a thermal printer is attached.

        All QR codes will contain key origin information in key expressions. If your coordinator cannot parse this information, it will not be capable of importing the wallet's fingerprint. As a result, Krux will not be able to sign transactions created by it unless you manually add the fingerprint so that it can be used to create Krux compatible PSBTs.

        "},{"location":"getting-started/usage/navigating-the-main-menu/#wallet-descriptor","title":"Wallet Descriptor","text":"

        When you select this option for the first time, you will be prompted to load a wallet. The camera will activate, and you will need to scan a wallet backup QR code generated by your wallet coordinator software. If the scan is successful, a preview of the wallet will be displayed for confirmation. If you abort the scan, you can alternatively load the wallet descriptor from an SD card.

        If you access this option again after having loaded your wallet, you will see the wallet's name, fingerprints and the abbreviated xPubs of all cosigners, along with a QR code containing the exact data that was initially loaded. If an SD card is inserted, you can save the descriptor to it. Additionally, if you have a thermal printer attached, you can print this QR code.

        Please note that once a wallet is loaded, it cannot be changed. To load a different wallet, you will need to restart the device and re-enter your mnemonic.

        "},{"location":"getting-started/usage/navigating-the-main-menu/#address","title":"Address","text":"

        Scan, verify, export or print your wallet addresses.

        "},{"location":"getting-started/usage/navigating-the-main-menu/#scan-address","title":"Scan Address","text":"

        This option turns on the camera and allows you to scan in a QR code of a receive address. Upon scanning, it will render its own QR code of the address back to the display along with the (text) address below it. You could use this feature to scan the address of someone you want to send coins to and display the QR back to your wallet coordinator rather than copy-pasting an address.

        If you have a thermal printer attached, you can also print this QR code.

        After proceeding through this screen, you will be asked if you want to check that the address belongs to your wallet. If you confirm, it will exhaustively search through as many addresses derived from your wallet as you want in order to find a match.

        This option exists as an extra security check to verify that the address your wallet coordinator has generated is authentic and belongs to your wallet.

        "},{"location":"getting-started/usage/navigating-the-main-menu/#receive-addresses","title":"Receive Addresses","text":"

        List your wallet receiving addresses, you can browse to select an arbitrary address to show your QRCode and print if you want

        "},{"location":"getting-started/usage/navigating-the-main-menu/#change-addresses","title":"Change Addresses","text":"

        List your wallet change addresses, you can browse to select an arbitrary address to show your QRCode and print if you want

        "},{"location":"getting-started/usage/navigating-the-main-menu/#sign","title":"Sign","text":"

        Under Sign, you can choose to sign a PSBT or a message. You can load both PSBTs and messages scanning QR codes or loading from files on a SD card.

        "},{"location":"getting-started/usage/navigating-the-main-menu/#psbt","title":"PSBT","text":"

        To sign a Bitcoin PSBT, you have the following options:

        1. Scan an Animated QR Code: Turn on the camera and scan an animated QR code of a PSBT generated by your wallet coordinator software.
        2. Load from SD Card: Load an unsigned PSBT file from your SD card.

        Upon loading the PSBT, you will be presented with a preview showing the amount of BTC being sent, the recipient's address, and the transaction fee. Amounts are displayed according to your locale and the International Bureau of Weights and Measures, while still adhering to the concept of the Satcomma standard format.

        If you choose to proceed and sign the transaction, the signed PSBT can be exported in two ways:

        1. As an animated QR code, which can be scanned back into your coordinator wallet.
        2. As a signed PSBT file, which can be saved to your SD card and then loaded back into your coordinator wallet for broadcasting.

        If a thermal printer is attached to your device, you can also print the PSBT QR codes for record-keeping or further processing.

        "},{"location":"getting-started/usage/navigating-the-main-menu/#message","title":"Message","text":"

        Similar to PSBTs, Krux can load, sign, and export signatures for messages. This feature allows you to attest not only to the ownership of the messages themselves but also to the ownership of Bitcoin addresses and the authorship of documents and files.

        "},{"location":"getting-started/usage/navigating-the-main-menu/#standard-messages-and-files","title":"Standard Messages and Files","text":"

        You can scan or load a file from an SD card, the content can be plaintext or the SHA-256 hash of a message. Upon loading, you will be shown a preview of the message's SHA-256 hash for confirmation before signing.

        If you confirm, a signature will be generated, and you will see a base64-encoded version of it. You can then choose to export it as a QR code or save it to an SD card. If a thermal printer is attached, you can also print the QR code.

        Following this, you will see and be allowed to export your raw (master) public key in hexadecimal form, which can be used by others to verify your signature. If a thermal printer is attached, you can also print this QR code.

        This feature is used to sign Krux releases, airgapped, using a Krux device.

        "},{"location":"getting-started/usage/navigating-the-main-menu/#messages-at-address","title":"Messages at Address","text":"

        Coordinators like Sparrow and Specter offer the possibility to sign messages at a Bitcoin receive address, allowing you to attest ownership of that address. Krux will detect if the message is of this type and present a similar workflow for signing. The main difference is that the address will be displayed along with the raw message, and since the message is signed with a derived address instead of the master public key, Krux won't offer the option to export the raw public key after the signature.

        "},{"location":"getting-started/usage/using-a-multisig-wallet/","title":"Using a Multisig Wallet","text":"

        This guide assumes you have already created a mnemonic for each cosigner that will be in your multisig wallet. If that is not the case, head over to the Generating a Mnemonic page and complete those steps first.

        When entering your mnemonics into Krux, make sure to select Multisig for all keys in your multisignature scheme. The choice of Single-sig vs. Multisig at this point will change the derivation path used to generate your master extended public key (xpub) which will affect how wallet software handles it.

        Selecting Multisig will derive an xpub using the derivation path m/48'/0'/0'/2' on mainnet and m/48'/1'/0'/2' on testnet, which indicates to wallet software that a Multisig Wallet script type should be used, specifically the Native Segwit script P2WSH, or just wsh.

        Note: While not required, the load of the Wallet Descriptor is strongly recommended to load your multisig wallet into Krux before signing a PSBT as it will allow the device to verify that the transaction it is signing has the correct list of cosigners as defined in the wallet. It is also useful if you wish to print a backup of the wallet or visually inspect the list of cosigners being output by your wallet coordinator.

        "},{"location":"getting-started/usage/using-a-multisig-wallet/#specter-desktop","title":"Specter Desktop","text":""},{"location":"getting-started/usage/using-a-multisig-wallet/#create-the-wallet","title":"Create the wallet","text":"

        In Specter Desktop, you will need to import your public key by adding a new device. Press the Add new device button on the left side of the app.

        Krux is not listed as one of the available device types on the Add Device screen, so you will need to select the Other option.

        You will be taken to the Upload Keys screen where you can choose to Scan QR code.

        On your Krux, navigate to the Extended Public Key option under the main menu and show the first QR code to Specter Desktop.

        It should import the xpub and display the Purpose as #0 Multisig Sig (Segwit).

        Give the device a name and press Continue. You should see the new device in the devices list on the left side of the app.

        Repeat this process for every key that will be in your multisig wallet.

        Once all key devices have been added, you can make a wallet using them. Press the Add new wallet button on the left side of the app.

        Choose to create a Multisignature wallet when it asks which type of wallet you want on the following screen.

        Select the devices you just added and press Continue.

        Give your wallet a name, make sure to select Segwit for the wallet type, and decide how many of your keys are required to sign a transaction, then press Create wallet.

        Congrats, you just created a multisig wallet!

        "},{"location":"getting-started/usage/using-a-multisig-wallet/#load-the-wallet-into-krux","title":"Load the wallet into Krux","text":"

        Load the wallet into Krux by going to the Settings page in Specter Desktop, then click the Export tab. There, press the Export button to display a QR code of your wallet.

        In Krux, select the Wallet Descriptor menu item option and scan the QR code.

        If it worked, Krux should display the wallet information that it loaded:

        "},{"location":"getting-started/usage/using-a-multisig-wallet/#receive-coins","title":"Receive coins","text":"

        The Receive screen should show your first receive address that you can send funds to.

        "},{"location":"getting-started/usage/using-a-multisig-wallet/#send-coins","title":"Send coins","text":"

        Go to Send in Specter Desktop, fill in the recipient address, amount, and any extra information you wish to supply, and click Create unsigned transaction.

        You will now see a screen listing the devices in your wallet. Select the device you want to sign the PSBT transaction with.

        Specter Desktop will display an animated QR code of the PSBT that you can scan with Krux by going to Sign > PSBT in its main menu. After scanning, Krux should display info about the transaction for you to confirm before signing.

        Once you have confirmed, Krux will begin animating a QR code of the signed transaction that you can scan into Specter Desktop.

        In Specter Desktop, click Scan signed transaction and show it the QR. Each part of the QR code that is read will receive a ghost icon to indicate progress.

        Once all parts of the QR code have been read, you should see a checkmark next to your device indicating its signature was added.

        Repeat this process for the remaining keys in your wallet until you have a quorum of signatures.

        After the final signature, a window will popup asking you to broadcast the transaction. Click Send transaction and your transaction should be broadcasted to the network!

        Congratulations, you have learned how to send a transaction using Specter Desktop \ud83c\udf89

        "},{"location":"getting-started/usage/using-a-multisig-wallet/#sparrow","title":"Sparrow","text":""},{"location":"getting-started/usage/using-a-multisig-wallet/#create-the-wallet_1","title":"Create the wallet","text":"

        In Sparrow, create a new wallet by going to File > New Wallet and give it a name.

        On the wallet screen, make sure to select a Multi Signature policy type with the Native Segwit (P2WSH) script type. Here you can decide how many keys will be in your multisig and how many should be required to sign a transaction.

        Now, you will need to import your public key. To do so, press the Airgapped Hardware Wallet button under Keystores section. On the screen that pops up, look for Krux option and click its Scan... button.

        On your Krux device, navigate to the Extended Public Key option in the main menu and click on XPUB - QR Code and show it to Sparrow.

        It should import the xpub and show a key tab under Keystores section:

        Repeat this process for every key that will be in your multisig wallet.

        Once all keys have been added, click the blue Apply button to create your wallet.

        Congrats, you just created a multisig wallet!

        "},{"location":"getting-started/usage/using-a-multisig-wallet/#load-the-wallet-output-descriptor-into-krux","title":"Load the wallet output descriptor into Krux","text":"

        Load the wallet output descriptor into Krux by going to the Settings screen of the wallet in Sparrow, then click the (QR code symbol) button at Script Policy section to the right of the Descriptor: to display a QR code that you can scan with Krux.

        In Krux, select the Wallet menu item option and scan the QR code.

        If it worked, Krux should display the wallet information that it loaded:

        "},{"location":"getting-started/usage/using-a-multisig-wallet/#receive-coins_1","title":"Receive coins","text":"

        Navigate to the Receive screen where you should see your first receive address that you can send funds to.

        "},{"location":"getting-started/usage/using-a-multisig-wallet/#send-coins_1","title":"Send coins","text":"

        Go to the Send screen, fill in the recipient address, amount, and any extra information you wish to supply, and click the blue Create Transaction button.

        On the next screen, make sure that the Signing Wallet is the one you created and that the Sighash is set to All. Click the blue Finalize Transaction for Signing button.

        On the next screen, click Show QR to make Sparrow display an animated QR code of the PSBT that you can scan with Krux by going to Sign > PSBT > Load from camera in its main menu.

        After scanning, Krux should display info about the transaction for you to confirm before signing.

        Once you have confirmed, Krux will begin animating a QR code of the signed transaction that you can scan into Sparrow.

        In Sparrow, click Scan QR and show it the QR. A progress bar will indicate how many parts of the QR have been read.

        Once all parts of the QR code have been read, you should see the signature bar partially fill indicating the signature was added.

        Repeat this process for the remaining keys in your wallet until you have a quorum of signatures.

        After the final signature, the bar will fill and two new buttons appear. Click the blue Broadcast Transaction button and your transaction should be broadcasted to the network!

        Congratulations, you have learned how to send a transaction using Sparrow \ud83c\udf89

        "},{"location":"getting-started/usage/using-a-multisig-wallet/#bluewallet","title":"BlueWallet","text":""},{"location":"getting-started/usage/using-a-multisig-wallet/#create-the-wallet_2","title":"Create the wallet","text":"

        In BlueWallet, create a new wallet by either pressing the + button or scrolling to the right until you see the Add now button.

        On the screen that pops up, add a name for your wallet, tap on Vault, and tap Create to begin creating a multisig wallet.

        On the following screen, you can proceed with the defaults or adjust the number of cosigners (default is 2-of-3) as needed by going into the Vault Settings. Make sure to leave the script type as p2wsh.

        Continue and you will be taken to a screen where you can import your keys.

        Tap Import under the first Vault Key.

        Tap again on Scan or import a file to begin scanning a QR code.

        On your Krux, navigate to the Extended Public Key option under the main menu and show the first QR code to BlueWallet.

        You should see a green checkmark next to the key if successful.

        Repeat this process for every key that will be in your multisig wallet.

        Once all keys have been added, tap Create.

        From here, you can send or receive.

        Congrats, you just created a multisig wallet!

        "},{"location":"getting-started/usage/using-a-multisig-wallet/#load-the-wallet-into-krux_1","title":"Load the wallet into Krux","text":"

        Load the wallet into Krux by tapping the ellipsis in the top-right to see the wallet settings.

        From here, tap Export Coordination Setup in order to display a QR code of your wallet.

        In Krux, select the Wallet Descriptor menu item option and scan the QR code.

        If it worked, Krux should display the wallet information that it loaded:

        "},{"location":"getting-started/usage/using-a-multisig-wallet/#receive-coins_2","title":"Receive coins","text":"

        Navigate to the Receive screen where you should see your first receive address that you can send funds to.

        "},{"location":"getting-started/usage/using-a-multisig-wallet/#send-coins_2","title":"Send coins","text":"

        Go to the Send screen, fill in the recipient address, amount, and any extra information you wish to supply, and tap Next.

        You should see an animated QR code of the PSBT that you can scan with Krux by going to Sign > PSBT in its main menu.

        After scanning, Krux should display info about the transaction for you to confirm before signing.

        Once you have confirmed, Krux will begin animating a QR code of the signed transaction that you can scan into BlueWallet.

        Once all parts of the QR code have been read, you can then choose to broadcast the transaction, sending it to the network!

        Congratulations, you have learned how to send a transaction using BlueWallet \ud83c\udf89

        "},{"location":"getting-started/usage/using-a-single-sig-wallet/","title":"Using a Single-sig Wallet","text":"

        This guide assumes you have already created a mnemonic. If that is not the case, head over to the Generating a Mnemonic page and complete those steps first.

        When entering your mnemonic into Krux, make sure to select Single-sig before proceeding. The choice of Single-sig vs. Multisig at this point will change the derivation path used to generate your master extended public key (xpub) which will affect how wallet software handles it.

        Selecting Single-sig will derive an xpub using the derivation path m/84'/0'/0' on mainnet and m/84'/1'/0' on testnet, which indicates to wallet software that a Segregated Witness (Segwit) script type should be used. For single-sig wallets, this script is P2WPKH, or just wpkh.

        Note: The load of the Wallet Descriptor step is unnecessary for signing PSBTs with Single-sig wallets since the script type (wpkh) and key are already known. However, this can be useful if you wish to print a backup of the wallet or want an additional sanity check.

        "},{"location":"getting-started/usage/using-a-single-sig-wallet/#specter-desktop","title":"Specter Desktop","text":""},{"location":"getting-started/usage/using-a-single-sig-wallet/#create-the-wallet","title":"Create the wallet","text":"

        In Specter Desktop, you will need to import your public key by adding a new device. Press the Add new device button on the left side of the app.

        Krux is not listed as one of the available device types on the Add Device screen, so you will need to select the Other option.

        You will be taken to the Upload Keys screen where you can choose to Scan QR code.

        On your Krux, navigate to the Extended Public Key option under the main menu and show the first QR code to Specter Desktop.

        It should import the xpub and display the Purpose as #0 Single Sig (Segwit).

        Give the device a name and press Continue. You should see the new device in the devices list on the left side of the app.

        After you've added a device with your key to Specter Desktop, you can make a wallet using it. Press the Add new wallet button on the left side of the app.

        Choose to create a Single key wallet when it asks which type of wallet you want on the following screen.

        Select the device you just added.

        Give your wallet a name and make sure to select Segwit for the wallet type, then press Create wallet.

        Congrats, you just created a single-sig wallet with your key!

        "},{"location":"getting-started/usage/using-a-single-sig-wallet/#load-the-wallet-into-krux","title":"Load the wallet into Krux","text":"

        Load the wallet into Krux by going to the Settings page in Specter Desktop, then click the Export tab. There, press the Export button to display a QR code of your wallet.

        In Krux, select the Wallet Descriptor menu item option and scan the QR code.

        If it worked, Krux should display the wallet information that it loaded:

        "},{"location":"getting-started/usage/using-a-single-sig-wallet/#receive-coins","title":"Receive coins","text":"

        Navigate to the Receive screen where you should see a receive address that you can send funds to.

        "},{"location":"getting-started/usage/using-a-single-sig-wallet/#send-coins","title":"Send coins","text":"

        Go to Send in Specter Desktop, fill in the recipient address, amount, and any extra information you wish to supply, and click Create unsigned transaction.

        You will now see a screen listing the devices in your wallet. Select the device you want to sign the PSBT transaction with.

        Specter Desktop will display an animated QR code of the PSBT that you can scan with Krux by going to Sign > PSBT in its main menu. After scanning, Krux should display info about the transaction for you to confirm before signing.

        Once you have confirmed, Krux will begin animating a QR code of the signed transaction that you can scan into Specter Desktop.

        In Specter Desktop, click Scan signed transaction and show it the QR. Each part of the QR code that is read will receive a ghost icon to indicate progress.

        Once all parts of the QR code have been read, you should see a window popup asking you to broadcast the transaction. Click Send transaction and your transaction should be broadcasted to the network!

        Congratulations, you have learned how to send a transaction using Specter Desktop \ud83c\udf89

        "},{"location":"getting-started/usage/using-a-single-sig-wallet/#sparrow","title":"Sparrow","text":""},{"location":"getting-started/usage/using-a-single-sig-wallet/#create-the-wallet_1","title":"Create the wallet","text":"

        In Sparrow, create a new wallet by going to File > New Wallet and give it a name.

        On the wallet screen, make sure to select a Single Signature policy type with the Native Segwit (P2WPKH) script type.

        Now, you will need to import your public key. To do so, press the Airgapped Hardware Wallet button under Keystores section. On the screen that pops up, look for Krux option and click its Scan... button.

        On your Krux device, navigate to the Extended Public Key option in the main menu and click on XPUB - QR Code and show it to Sparrow.

        It should import the xpub and show a key tab under Keystores section:

        If everything looks right, click the blue Apply button to create your wallet.

        Congrats, you just created a single-sig wallet with your key!

        "},{"location":"getting-started/usage/using-a-single-sig-wallet/#load-the-wallet-output-descriptor-into-krux-optional","title":"Load the wallet output descriptor into Krux (optional)","text":"

        Single-sig wallets don't need to perform this step, it is optional! Load the wallet output descriptor into Krux by going to the Settings screen of the wallet in Sparrow, then click the (QR code symbol) button at Script Policy section to the right of the Descriptor: to display a QR code that you can scan with Krux.

        In Krux, select the Wallet menu item option and scan the QR code.

        If it worked, Krux should display the wallet information that it loaded:

        "},{"location":"getting-started/usage/using-a-single-sig-wallet/#receive-coins_1","title":"Receive coins","text":"

        Navigate to the Receive screen where you should see a receive address that you can send funds to.

        "},{"location":"getting-started/usage/using-a-single-sig-wallet/#send-coins_1","title":"Send coins","text":"

        Go to the Send screen, fill in the recipient address, amount, and any extra information you wish to supply, and click the blue Create Transaction button.

        On the next screen, make sure that the Signing Wallet is the one you created and that the Sighash is set to All. Click the blue Finalize Transaction for Signing button.

        On the next screen, click Show QR to make Sparrow display an animated QR code of the PSBT that you can scan with Krux by going to Sign > PSBT > Load from camera in its main menu.

        After scanning, Krux should display info about the transaction for you to confirm before signing.

        Once you have confirmed, Krux will begin animating a QR code of the signed transaction that you can scan into Sparrow.

        In Sparrow, click Scan QR and show it the QR. A progress bar will indicate how many parts of the QR have been read.

        Once all parts of the QR code have been read, you should see the signature bar fill and two new buttons appear. Click the blue Broadcast Transaction button and your transaction should be broadcasted to the network!

        Congratulations, you have learned how to send a transaction using Sparrow \ud83c\udf89

        "},{"location":"getting-started/usage/using-a-single-sig-wallet/#bluewallet","title":"BlueWallet","text":""},{"location":"getting-started/usage/using-a-single-sig-wallet/#create-the-wallet_2","title":"Create the wallet","text":"

        In BlueWallet, create a new wallet by either pressing the + button or scrolling to the right until you see the Add now button.

        On the screen that pops up, tap Import wallet to import your public key.

        On the following screen, tap Scan or import a file and it will begin trying to scan a QR code.

        On your Krux, navigate to the Extended Public Key option under the main menu and make sure to show the second, zpub QR code to BlueWallet.

        It should import the key and create a watch-only wallet. From here, you can send or receive.

        Congrats, you just created a single-sig wallet with your key!

        "},{"location":"getting-started/usage/using-a-single-sig-wallet/#load-the-wallet-into-krux_1","title":"Load the wallet into Krux","text":"

        Load the wallet into Krux by tapping the ellipsis in the top-right to see the wallet settings.

        From here, tap Export/Backup in order to display a QR code of your wallet.

        In Krux, select the Wallet Descriptor menu item option and scan the QR code.

        If it worked, Krux should display the wallet information that it loaded:

        "},{"location":"getting-started/usage/using-a-single-sig-wallet/#receive-coins_2","title":"Receive coins","text":"

        Navigate to the Receive screen where you should see a receive address that you can send funds to.

        Note that you can verify the receive address belongs to your wallet by using the Scan Address option.

        "},{"location":"getting-started/usage/using-a-single-sig-wallet/#send-coins_2","title":"Send coins","text":"

        Go to the Send screen, fill in the recipient address, amount, and any extra information you wish to supply, and tap Next.

        You should see an animated QR code of the PSBT that you can scan with Krux by going to Sign > PSBT in its main menu.

        After scanning, Krux should display info about the transaction for you to confirm before signing.

        Once you have confirmed, Krux will begin animating a QR code of the signed transaction that you can scan into BlueWallet.

        Once all parts of the QR code have been read, you can then choose to broadcast the transaction, sending it to the network!

        Congratulations, you have learned how to send a transaction using BlueWallet \ud83c\udf89

        "},{"location":"snippets/12th-24th-word-generate/","title":"12th 24th word generate","text":"

        On your 12th or 24th word, you can leave it blank to have Krux generate the final checksum word of your mnemonic for you. This is useful when you choose a manual method to generate your mnemonic and want the final BIP-39 checksum word to be valid.

        "},{"location":"snippets/amigo-more-info-faq/","title":"Amigo more info faq","text":""},{"location":"snippets/amigo-more-info-faq/#a-note-about-the-maix-amigo","title":"A note about the Maix Amigo","text":"

        Some Amigo screens have inverted X coordinates, others display colors differently. For more info see FAQ

        "},{"location":"snippets/flash-krux-logo/","title":"Flash krux logo","text":"

        When the flashing process completes, you should see the Krux logo:

        If it doesn't, try turning your device off and on by holding down the power button for six seconds.

        Congrats, you're now running Krux!

        "},{"location":"snippets/m5stickv-usb-c/","title":"M5stickv usb c","text":"

        M5stickV's USB-C port lacks pull up resistors required for it to be recognized and powered by host (computer) USB-C ports. If you don't have an USB-A available, you can use a USB hub connected between your computer's USB-C and M5stickV.

        "},{"location":"snippets/sd-card-info-faq/","title":"Sd card info faq","text":"

        We cannot guarantee that a microSD card is compatible and will work in your device; you'll need to test it on the device to be sure, read the FAQ for more info.

        "},{"location":"snippets/single-multi-wallet-descriptor/","title":"Single multi wallet descriptor","text":"

        In Krux, select the Wallet Descriptor menu item option and scan the QR code.

        If it worked, Krux should display the wallet information that it loaded:

        "},{"location":"snippets/tips-after-install/","title":"Tips after install","text":""},{"location":"snippets/tips-after-install/#multilingual-support","title":"Multilingual support","text":"

        Prefer a different language? Krux has support for multiple languages. Once at the start screen, go to Settings, followed by Locale, and select the locale you wish to use.

        "},{"location":"snippets/tips-after-install/#upgrade-via-microsd-card","title":"Upgrade via microSD card","text":"

        Once you've installed the initial firmware on your device via USB, you can either continue updating the device by flashing or you can perform upgrades via microSD card to keep the device airgapped.

        "},{"location":"snippets/sparrow/broadcast-sparrow/","title":"Broadcast sparrow","text":"

        Click the blue Broadcast Transaction button and your transaction should be broadcasted to the network!

        Congratulations, you have learned how to send a transaction using Sparrow \ud83c\udf89

        "},{"location":"snippets/sparrow/import-xpub-sparrow/","title":"Import xpub sparrow","text":"

        Now, you will need to import your public key. To do so, press the Airgapped Hardware Wallet button under Keystores section. On the screen that pops up, look for Krux option and click its Scan... button.

        On your Krux device, navigate to the Extended Public Key option in the main menu and click on XPUB - QR Code and show it to Sparrow.

        It should import the xpub and show a key tab under Keystores section:

        "},{"location":"snippets/sparrow/load-wallet-output-sparrow/","title":"Load wallet output sparrow","text":"

        In Krux, select the Wallet menu item option and scan the QR code.

        If it worked, Krux should display the wallet information that it loaded:

        "},{"location":"snippets/sparrow/send-coins-sparrow/","title":"Send coins sparrow","text":""},{"location":"snippets/sparrow/send-coins-sparrow/#send-coins","title":"Send coins","text":"

        Go to the Send screen, fill in the recipient address, amount, and any extra information you wish to supply, and click the blue Create Transaction button.

        On the next screen, make sure that the Signing Wallet is the one you created and that the Sighash is set to All. Click the blue Finalize Transaction for Signing button.

        On the next screen, click Show QR to make Sparrow display an animated QR code of the PSBT that you can scan with Krux by going to Sign > PSBT > Load from camera in its main menu.

        After scanning, Krux should display info about the transaction for you to confirm before signing.

        Once you have confirmed, Krux will begin animating a QR code of the signed transaction that you can scan into Sparrow.

        In Sparrow, click Scan QR and show it the QR. A progress bar will indicate how many parts of the QR have been read.

        "}]} \ No newline at end of file +{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"faq/","title":"FAQ","text":""},{"location":"faq/#is-krux-a-hardware-wallet","title":"Is Krux a hardware wallet?","text":"

        The term \"hardware wallet\" typically refers to devices dedicated to storing private keys and signing transactions. These devices often feature specific security components like secure element chips.

        Krux was initially developed as a signer, operating exclusively in amnesic mode, which requires users to load their keys each time the device is powered on. However, Krux has evolved and now offers the option to store mnemonics, similar to traditional hardware wallets. These mnemonics can be stored in the device's internal memory or on SD cards.

        Krux does not include hardware secure elements. The security of stored data relies on encryption.

        Note: Due to the inherent fragility of electronic components, never use your Krux device or SD card encrypted storage as your sole backup method. Always maintain a physical backup for added security.

        "},{"location":"faq/#what-is-beta-version","title":"What is Beta version?","text":"

        The Beta version includes the latest and most experimental features, which we occasionally share on our social media. These can be found exclusively in the test (beta) repository. Use and flash the beta firmware if you are curious about new features or want to participate in the development process by hunting bugs, providing feedback, and sharing ideas in our Telegram groups or other social media platforms.

        For regular use, flash the official releases, which are signed, thoroughly tested, and well-documented.

        "},{"location":"faq/#what-is-krux-android-app","title":"What is Krux Android app?","text":""},{"location":"faq/#how-can-i-find-it","title":"How can I find it?","text":"

        The Krux Android app is available as an APK in the test (beta) repository. It requires Android 6.0 or above.

        "},{"location":"faq/#how-can-i-install-it","title":"How can I install it?","text":"

        The APK is not available on the Play Store. You can download the APK directly or transfer it to your Android device via SD card or USB cable. To install it, you may need to configure your Android device to allow installations from unknown sources.

        "},{"location":"faq/#is-it-safe-to-use","title":"Is it safe to use?","text":"

        The Krux Android app is designed for learning about Krux and Bitcoin air-gapped transactions. Due to the numerous potential vulnerabilities inherent in smartphones, such as the lack of control over the operating system, libraries, and hardware peripherals, the Krux app should NOT be used to manage wallets containing savings or important keys and mnemonics. For secure management of your keys, a dedicated device is recommended.

        "},{"location":"","title":"Krux","text":"

        Krux is an open-source firmware that transforms off-the-shelf Kendryte K210 devices, such as the Maix Amigo, M5StickV and more, into versatile bitcoin transaction signers. Beyond its core functionality, Krux is a flexible platform that can adapt to devices with different form factors, providing a suite of tools to assist with the creation and recovery of mnemonic backups, some of which include encryption options for enhanced security.

        Devices like the Maix Amigo and Yahboom come ready to use, with large touchscreens that make it easy and user-friendly to operate. These devices are ideal for those looking for a plug-and-play solution. On the other hand, Krux also supports development board kits, which are perfect for DIY enthusiasts who enjoy customizing and building their own hardware setups.

        Interacting seamlessly with leading coordinator wallets through QR codes, SD cards, and even thermal printers, the user-friendly firmware offers unique features to support transactions and mnemonic backups in an offline environment.

        To learn more about Krux, check out Getting Started.

        "},{"location":"parts/","title":"Devices and Parts List","text":""},{"location":"parts/#krux-compatible-devices","title":"Krux Compatible Devices","text":""},{"location":"parts/#comparative-table","title":"Comparative Table","text":"Device M5StickV Maix Amigo Maix Dock Maix Bit Yahboom k210 module Maix Cube Price range US$ 50-55 US$ 50-85 US$ 27-35 US$ 32-42 US$ 45-61 US$ 34-49 Screen size / resolution 1.14\" / 135*240 3.5\" / 320*480 2.4\" / 240*320 2.4\" / 240*320 2\" / 240*320 1.3\" / 240*240 Brightness control Device size 48*24*22mm 104*63*17mm 98*59*18mm 69*84*41mm 57*41*17mm 40*40*16mm Touchscreen Capacitive Capacitive Camera OV7740 OV7740 rearGC0328 front GC0328 OV2640 orOV5642 OV2640 (VER:1.0) orGC2145 (VER:1.1) OV7740 Battery 200mAh 520mAh 200mAh Requirements None None Rotary encoder 3D printed case SolderingAssembly Buttons 3D printed case SolderingAssembly None None Warnings Camera has lens distortion Micro USB 3-Way button

        : Only OV7740 and OV2640 have an anti-glare mode to better capture images from high brightness screens or with incident light.

        : M5StickV's USB-C port lacks pull up resistors required for it to be recognized and powered by host (computer) USB-C ports. If you don't have an USB-A available, you can use a USB hub connected between your computer's USB-C and M5StickV.

        : Some Amigo screens have inverted X coordinates, others display colors differently. For more info see Troubleshooting.

        : Some stores ship the Maix Dock with soldered pin connectors that do not fit into the 3D printed case.

        All devices feature Kendryte K210 chip: 28nm process, dual-core RISC-V 64bit @400MHz, 8 MB high-speed SRAM, DVP camera and MCU LCD interface, AES Accelerator, SHA256 Accelerator, FFT Accelerator.

        "},{"location":"parts/#m5stickv","title":"M5StickV","text":"

        Below is a list of some distributors where you can find this device:

        • M5Stack
        • Mouser
        • Digi-Key
        • Electromaker
        • Lee's Electronic
        • AliExpress
        • ABRA
        • Adafruit
        • Cytron
        "},{"location":"parts/#maix-amigo","title":"Maix Amigo","text":"

        Below is a list of some distributors where you can find this device:

        • AliExpress
        • Seeed Studio
        • Digi-Key
        • Mouser
        • Electromaker
        "},{"location":"parts/#yahboom-k210-module","title":"Yahboom k210 module","text":"

        It comes with a compatible 32G card, an USB card reader, one PH2.0 4Pin male-to-male connector and one PH2.0 female adapter (to connect to a thermal printer). Below is a list of some distributors where you can find this device:

        • AliExpress
        • Amazon
        • Yahboom Store
        • ETC HK Shop
        "},{"location":"parts/#maix-cube","title":"Maix Cube","text":"

        Below is a list of some distributors where you can find this device:

        • Seeed Studio
        • Mouser
        • Electromaker
        • Digi-Key
        • AliExpress
        • Amazon
        "},{"location":"parts/#maix-dock-and-maix-bit","title":"Maix Dock and Maix Bit","text":"

        For the DIYers, the Maix Dock and Maix Bit are also supported but will require sourcing the parts individually and building the device yourself.

        Below are example implementations with instructions on how to recreate them:

        • https://github.com/selfcustody/DockEncoderCase
        • https://github.com/selfcustody/MaixBitCase

        Below is a list of some distributors where you can find these devices:

        • Mouser
        • Electromaker
        • Digi-Key
        • AliExpress
        • Amazon
        "},{"location":"parts/#other-parts","title":"Other Parts","text":""},{"location":"parts/#usb-c-or-micro-usb-charge-cable","title":"USB-C or Micro USB Charge Cable","text":"

        This will come with the device. It will be necessary to power, charge the device (if it has battery) and to initially flash the firmware.

        "},{"location":"parts/#optional-microsd-card","title":"(Optional) MicroSD Card","text":"

        We cannot guarantee that a microSD card is compatible and will work in your device; you'll need to test it on the device to be sure, read the FAQ for more info. Yahboom will come with a compatible 32G card. The size of the SD card isn't important; anything over a few megabytes will be plenty.

        "},{"location":"parts/#optional-ttl-serial-thermal-printer","title":"(Optional) TTL Serial Thermal Printer","text":"

        Warning/Disclaimer: This feature is intended for individuals with experience in electronics tinkering and soldering.

        Krux has the capability to print all QR codes it generates, including those for mnemonics, xpubs, wallet backups, and signed PSBTs, using a locally-connected thermal printer via its serial port.

        Many TTL serial thermal printers may be compatible, but currently, the Goojprt QR203 has the best support. The Adafruit printer starter pack can also be a convenient option to get started, as it includes all the necessary components for printing (except the conversion cable). To ensure proper functionality, enable the printer driver in the Krux settings, set the Tx pin and baud rate value to either 19200 or 9600 (depends on the printer), as explained in this Adafruit printer tutorial. You will need to connect the device's Tx to the printer's Rx and device's ground to the printer's ground, do not connect any other pins because a wrong connection may damage your device. The printer requires a dedicated power supply, typically with an output of 5 to 9V and capable of supplying at least 2A. For more information, see this discussion.

        "},{"location":"parts/#optional-conversion-cable-for-thermal-printer","title":"(Optional) Conversion Cable for Thermal Printer","text":"

        To connect the printer to M5StickV, Amigo or Cube, you will need a grove conversion cable with a 4-pin male Grove connector on one end (to connect to the device) and 4-pin male jumpers on the other end (to connect to the printer). Check your device and printer model connection first, Yahboom comes with PH2.0 4Pin female connector; Dock and Bit doesn't have a connector. For a more reliable connection, it is recommended to cut and solder the wires of your custom cables instead of using jumpers.

        "},{"location":"support/","title":"Support the Project","text":""},{"location":"support/#ways-you-can-help","title":"Ways you can help","text":""},{"location":"support/#development","title":"Development","text":"

        Audit the code, file an issue, make a pull request, or do all three. :)

        "},{"location":"support/#documentation","title":"Documentation","text":"

        \"I'd like to see Krux help as many people as possible, and to do that, good documentation is needed. If you identify a better way to say something, please make a PR, any help is appreciated.\" - Jeff

        "},{"location":"support/#translation","title":"Translation","text":"

        Krux supports different languages. If you missed a language or saw an awkward translation, open an issue or make a PR! You can also make a difference by translating this documentation! For information on how to translate, see here

        "},{"location":"support/#social","title":"Social","text":"

        Reach out via our Telegram group or X profile (Twitter) for faster help, share ideas and join the Krux community. Help others get to know Krux.

        \"I'm an engineer, not a marketer. If you like Krux, help spread the word!\" - Jeff

        "},{"location":"support/#donations","title":"Donations","text":"

        Krux won't ask for, receive, manage or distribute donations. You can donate directly to a Krux contributor of your choice.

        "},{"location":"troubleshooting/","title":"Troubleshooting","text":""},{"location":"troubleshooting/#before-installing","title":"Before Installing","text":""},{"location":"troubleshooting/#linux-os-not-listing-serial-port","title":"Linux OS not listing serial port?","text":"

        If you get the following error when trying to flash your device: Failed to find device via USB. Is it connected and powered on? Make sure your device is being detected and serial ports are being mounted by running:

        ls /dev/ttyUSB*\n
        Expect one port to be listed for devices like M5StickV and Maix Dock /dev/ttyUSB0, and two ports for Maix Amigo and Maix Bit /dev/ttyUSB0 /dev/ttyUSB1.

        If you don't see them, your OS may not be loading the correct drivers to create the serial ports to connect to. Ubuntu has a known bug where the brltty driver \"kidnaps\" serial devices. You can solve this problem by removing it:

        sudo apt-get remove brltty\n

        "},{"location":"troubleshooting/#m5stickv-device-not-being-recognized-and-charged","title":"M5StickV device not being recognized and charged?","text":"

        M5StickV's USB-C port lacks pull up resistors required for it to be recognized and powered by host (computer) USB-C ports. If you don't have an USB-A available, you can use a USB hub connected between your computer's USB-C and M5StickV.

        "},{"location":"troubleshooting/#device-not-charging-or-being-recognized","title":"Device not charging or being recognized?","text":"

        If you have a Maix Amigo, make sure you're using the USB-C port at the bottom of the device, not the one on the left side.

        Different computer hosts have varying hardware, operating systems, and behaviors regarding connecting to their USB ports. Below are the expected behaviors:

        USB-A:

        Your device should charge and turn on when connected to a USB-A port, even if it was initially turned off. You can also turn off the device while it continues to charge. However, some hosts' USB-A ports may behave like USB-C ports, as described below.

        USB-C:

        • If the device is turned off and connected to a USB-C port, it should turn on and start charging. You can turn it off again, and it will continue to charge.

        • If the device is already turned on and connected to a USB-C port, it may not charge or be recognized by the computer. In this case, turn off the device to initiate recognition and charging. Once turned off and reconnected, the device should restart, be recognized by the computer, and charging should be triggered by USB-C hosts. If your device is not charging or being recognized as expected, try using a different USB port or a different computer to determine if the issue is with the device or the host's USB port.

        "},{"location":"troubleshooting/#device-randomly-freezes-or-restarts","title":"Device randomly freezes or restarts?","text":"

        If the device behaves this way when connected to the computer, Windows is known to have issues with USB-C devices. If you are experiencing random crashes or even reboots and your device does not have a battery, try using a phone charger or other power source such as a power bank.

        "},{"location":"troubleshooting/#after-installing","title":"After Installing","text":""},{"location":"troubleshooting/#maix-amigo-touchscreen-doesnt-work-with-v24030-but-worked-okay-with-v23091","title":"Maix Amigo touchscreen doesn't work with v24.03.0 but worked okay with v23.09.1?","text":"

        We added a hardware IRQ (interrupt request) to the firmware, so when you open your Maix Amigo, you will see a switch in the middle of the device board, it must be in the upper position for the touchscreen to work with v24.03.0 and later.

        "},{"location":"troubleshooting/#troubleshooting-lcd-settings-on-maix-amigo","title":"Troubleshooting LCD Settings on Maix Amigo","text":"

        Buttons in the Wrong Order

        If the buttons on keypad input screens appear to be in the wrong order, this might be due to inverted X coordinates. To correct this:

        1. Go to Settings > Hardware > Display.
        2. Change the value of Flipped X Coordinates.

        Incorrect Colors

        If the colors displayed on the interface or camera preview are incorrect, you can try the following options:

        Inverted Colors

        If, for example, the background color is white when it should be black, go to Settings > Hardware > Display and toggle Inverted Colors .

        BGR Colors

        If, for example, you are using the Orange theme, and instead of orange the colors appear bluish, toggle BGR Colors in the display settings.

        LCD Type

        WARNING! Only try changing this setting if you failed to fix colors with previous ones.

        If adjusting BGR Colors and Inverted Colors doesn't fix the color issue, try changing the LCD Type:

        1. After changing this setting, you will be warned that the device will reboot automatically if this change does not resolve the issue (if it fails, the screen will turn black, and you'll not see anything).
        2. After proceeding with the warning, if you see a message prompting you to press the PREVIOUS (UP) button, it means that the new setting worked. Follow the instructions and press UP.
        3. If you notice menu options but the colors are still wrong, try again with different combinations of Inverted Colors and BGR Colors. This time, it is likely you will find a combination that correctly displays the colors of themes and the camera feed.

        If, after the warning in step 1, the screen turns black and you don't see anything, don't panic or press any buttons. Just wait 5 seconds, and the device will automatically reboot with the previous display settings. This means you should keep the default LCD Type setting and maybe try again with Inverted Colors and BGR Colors.

        If you accidentally pressed PREVIOUS (UP) and saved the wrong setting, you will have to perform a wipe to remove all stored settings to be able to see the screen working again. On Linux, go to the folder where you downloaded the Krux firmware and use Ktool to fully wipe your device:

        ./ktool-linux -B goE -b 1500000 -E

        (Soon Krux-Installer will have a full wipe button too)

        Then flash the firmware again.

        ./ktool-linux -B goE -b 1500000 maixpy_amigo/kboot.kfpkg

        "},{"location":"troubleshooting/#device-didnt-reboot-and-screen-is-blank","title":"Device didn't reboot, and screen is blank","text":"

        If the device didn't reboot after successfully flashing the firmware, and the screen is blank after turning it off and on, check if the downloaded file matches the device (this can also occur due to data corruption). Try downloading binaries again.

        You can also install MaixPy IDE to help with debugging, Tools > Open Terminal > New Terminal > Connect to serial port > Select a COM port available (if it doesn't work, try another COM port). It will show the terminal and some messages, a message about an empty device or with corrupted firmware appears like: \"interesting, something's wrong, boot failed with exit code 233, go to find your vendor.\"

        "},{"location":"troubleshooting/#usage","title":"Usage","text":""},{"location":"troubleshooting/#why-isnt-krux-scanning-the-qr-code","title":"Why isn't Krux scanning the QR code?","text":"

        The level of detail that you see is what Krux sees. If the QR code shown on the device's screen is blurry, the camera lens of the device may be out of focus. It can be adjusted by rotating it clockwise or counter-clockwise to achieve a clearer result. The lenses usually comes with a drop of glue that makes id harder to adjust for the first time. You can use your fingertip, tweezers or small precision pliers to help, being careful to don't damage the fragile lenses.

        If you have adjusted the lens already, the device may be too far away or too close to the code to read it. Start by holding the device as close to the QR code as possible and pulling away slowly until all or most of the QR code is viewable within the screen. If the code on the screen looks crisp, Krux should read it quickly and give you immediate feedback.

        If you are in a dark environment, you can hold down the ENTER button of the M5StickV or Maix Amigo to turn on their LED light to potentially increase visibility. Some cameras (OV7740 and OV2640) have an anti-glare mode to better capture images from high brightness screens or with incident light, they are present on M5StickV, Amigo, Cube and some Yahboom. To enable/disable the anti-glare mode on a supported device just press the PAGE button while scanning.

        "},{"location":"troubleshooting/#error-when-scanning-qr-code","title":"Error when scanning QR code?","text":"

        If Krux is recognizing that it sees a QR code but is displaying an error message after reading it, the likely reason is that the QR code is not in a format that Krux works with. We have listed the supported formats below:

        For BIP-39 mnemonics:

        • BIP-39 Plaintext (Used by Krux and https://iancoleman.io/bip39/)
        • SeedSigner SeedQR and CompactSeedQR Formats
        • UR Type crypto-bip39
        • Encrypted QR Code (Format created by Krux, more information here)

        For Wallet output descriptor:

        • JSON with at least a descriptor key containing an output descriptor string
        • Key-value INI files with at least Format, Policy, and Derivation keys
        • UR Type crypto-output

        For PSBT (Partially Signed Bitcoin Transactions):

        • Base43, Base58, and Base64-encoded bytes
        • Raw Bytes
        • UR Type crypto-psbt

        Additionally, Krux recognizes animated QR codes that use either the plaintext pMofN (the Specter QR format) or binary UR encodings.

        "},{"location":"troubleshooting/#computer-not-reading-qr-code-that-krux-displays","title":"Computer not reading QR code that Krux displays?","text":"

        You can toggle brightness of QR codes from public keys and PSBTs by pressing PAGE button. In the future, more work will be done to support displaying lower density QR codes. If you are using an M5StickV, the small screen makes it difficult for laptop webcams to capture enough detail to parse the QR codes it displays.

        For now, a workaround you can do is to take a picture or video of the QR code with a better-quality camera (such as your phone), then enlarge and display the photo or video to your webcam. Alternatively, it may be simpler to use a mobile wallet such as BlueWallet with the M5StickV since phone cameras don't seem to have issues reading the small QR codes. You can also save the PSBT on a microSD card for Krux to sign and then save the signed transaction to the microSD card to transfer the file to the computer or phone.

        "},{"location":"troubleshooting/#why-does-krux-say-the-entropy-of-my-fifty-dice-rolls-does-not-contain-128-bits-of-entropy","title":"Why Does Krux Say the Entropy of My Fifty Dice Rolls Does Not Contain 128 Bits of Entropy?","text":"

        Please check how entropy measurement works.

        "},{"location":"uncommon-questions/","title":"Uncommon Questions","text":""},{"location":"uncommon-questions/#what-are-all-the-features-available","title":"What are all the features available?","text":"

        On the official releases page you will find all the features listed, with details on the Getting Started page with a brief summary on the Navigation Overview page.

        "},{"location":"uncommon-questions/#what-is-the-purpose-of-using-an-sd-card-with-the-device","title":"What is the purpose of using an SD card with the device?","text":"

        SD card use is optional, but can be used to upgrade the firmware, save settings, cnc/file, QR codes, XPUBs, encrypted mnemonics, and also to save and load PSBTs, messages and wallet output descriptors.

        "},{"location":"getting-started/","title":"Getting Started","text":"

        Krux is open-source Bitcoin signing firmware for devices with the K210 chipset; also known as a hardware signer.

        Signing operations in Krux are done offline via QR code or via SD card using the PSBT functionality. You can create/load your BIP-39 mnemonic, or import a wallet descriptor, and sign transactions all without having to plug the device into your computer (except to initially install the firmware). It reads QR codes with its camera and outputs QR codes to its screen, or to paper via an optional thermal printer attachment.

        Krux runs offline, and therefore never handles the broadcasting part of the PSBT transaction. Instead, you can use Krux with third-party wallet coordinators to broadcast transactions from your online computer or mobile device while keeping your keys offline. Krux was built to be vendor agnostic and works with many popular wallet coordinators, including:

        • Sparrow Wallet (desktop)
        • Specter Desktop (desktop)
        • Nunchuk (mobile)
        • BlueWallet (mobile)
        "},{"location":"getting-started/navigation/","title":"Navigation Overview","text":"

        Below is the mind map representation of the currently menus available. Click the circle with a number (Ex.: ) to the right of each node to expand and explore. Also, enable full screen in the top right menu for better viewing .

        "},{"location":"getting-started/navigation/#login-menu","title":"Login Menu","text":""},{"location":"getting-started/navigation/#home-menu-loaded-a-mnemonic","title":"Home Menu (Loaded a mnemonic)","text":""},{"location":"getting-started/settings/","title":"Settings","text":"

        In the Krux home menu, there is a Settings entry. Some submenu entries have too many options to fit on one screen, swipe up or down to navigate between the screens if your device has a touchscreen. Below is a breakdown of the options you can change:

        "},{"location":"getting-started/settings/#default-wallet","title":"Default Wallet","text":"

        Set the default attributes for wallet loading.

        "},{"location":"getting-started/settings/#multisig","title":"Multisig","text":"

        Set this to true if you are more likely to use Krux for multisig setups. This way, you won't need to \"Customize\" your wallet attributes every time you load a key.

        "},{"location":"getting-started/settings/#network","title":"Network","text":"

        This option allows you to switch between mainnet (the default) and testnet. Testnet can be used to try out different wallet coordinators or for development.

        "},{"location":"getting-started/settings/#encryption","title":"Encryption","text":"

        Modify the encryption method and parameters to fit your needs. This will be used when storing encrypted mnemonics or creating encrypted QR codes. For more info see Krux Encrypted Mnemonics.

        "},{"location":"getting-started/settings/#pbkdf2-iter-iterations","title":"PBKDF2 Iter. (Iterations)","text":"

        When you enter the encryption key, it is not directly used to encrypt your data. In order to protect against brute force attacks, the key is derived multiple times using hashing functions. PBKDF2 (Password-Based Key Derivation Function) iterations stands for the amount of derivations that will be performed over your key prior to encrypt/decrypt your mnemonic.

        If you increase this value it will make the encryption harder, at the cost of taking longer to encrypt/decrypt your mnemonics.

        Values must be multiple of 10,000. This was done to save data space on QR codes.

        "},{"location":"getting-started/settings/#encryption-mode","title":"Encryption Mode","text":"

        Choose between well known and widely used AES (Advanced Encryption Standard) modes:

        "},{"location":"getting-started/settings/#aes-ecb","title":"AES-ECB","text":"

        ECB (Electronic Codebook) is a simpler method where data blocks are encrypted individually. Compared to CBC, it will be faster and simpler to encrypt, QR codes will have a lower density and will be easier to transcribe.

        "},{"location":"getting-started/settings/#aes-cbc","title":"AES-CBC","text":"

        CBC (Cipher-block Chaining) is considered more secure than ECB. The first data block, an initialization vector (IV), is used to add random data to the encryption. The encryption of subsequent blocks depends on the data from previous blocks, ensuring chaining.

        Encryption will take longer because a snapshot will be needed to generate the IV. This IV will be stored together with the encrypted data, making encrypted QR codes denser and harder to transcribe.

        "},{"location":"getting-started/settings/#hardware","title":"Hardware","text":"

        Customize the parameters available for your device and change printer settings.

        "},{"location":"getting-started/settings/#encoder-maix-dock-only","title":"Encoder (Maix Dock only)","text":"

        If your device has a rotary encoder, you can change the debounce threshold in milliseconds. With lower values, faster movements and navigation will be allowed.

        The caveat is low values can cause issues, such as double step and unexpected movements, especially with lower quality encoders. If this is the case increase the value to make navigation more stable.

        "},{"location":"getting-started/settings/#display-maix-amigo-only","title":"Display (Maix Amigo only)","text":"

        Some Maix Amigo screens are different, here you can customize the BGR Colors, Flipped X Coordinates, Inverted Colors and LCD Type. For more info see Troubleshooting

        "},{"location":"getting-started/settings/#printer","title":"Printer","text":"

        You can set up a TTL serial thermal printer or tell Krux to store a GRBL CNC instructions file on a SD card to machine QR codes.

        "},{"location":"getting-started/settings/#cnc","title":"CNC","text":"

        Define several machining parameters according to the desired size, material you'll use, and your CNC characteristics and capabilities.

        "},{"location":"getting-started/settings/#thermal","title":"Thermal","text":"

        Printers can come with different baudrates from the manufacturer. By default, Krux assumes the connected printer will have a baudrate of 9600. If yours is different, you can change it here.

        Also setup the TX Pin you'll use (e.g. 35 for M5StickV and 7 for Maix Amigo) and tweak other parameters according to your printer recommendations. For most printers you will only need to connect 2 cables, the device TX to the printer RX and ground. Current uses of printing are listed here. Consult the parts list for supported printers.

        "},{"location":"getting-started/settings/#driver","title":"Driver","text":"

        Here you choose between Thermal, CNC or none (default). Leave this setting to \"none\" if you won't use a printer and don't want to be bothered by print prompts.

        "},{"location":"getting-started/settings/#touchscreen-maix-amigo-and-yahboom-only","title":"Touchscreen (Maix Amigo and Yahboom only)","text":"

        If your device has touchscreen you can change the touch detection threshold. If it is being too sensitive or detecting false or ghost touches, you should increase the threshold value, making it less sensitive. The other way is also valid, reduce the threshold to make the screen more sensitive to touches.

        "},{"location":"getting-started/settings/#language-locale","title":"Language - Locale","text":"

        Here you can change Krux to your desired language.

        "},{"location":"getting-started/settings/#persist","title":"Persist","text":"

        Choose between flash (device's internal memory) or SD card for the place where your settings will be stored.

        "},{"location":"getting-started/settings/#security","title":"Security","text":"

        Adjust settings that may impact your security protocols.

        "},{"location":"getting-started/settings/#shutdown-time","title":"Shutdown Time","text":"

        Set the time it takes for Krux to automatically shut down. This feature not only conserves your device's battery, if it has one, but also serves as an important security measure. If you forget your device with private keys loaded, it will shut down automatically after the set time.

        Please note that devices without batteries and power management will not shut down but will reboot instead, which is sufficient to unload private keys.

        "},{"location":"getting-started/settings/#hide-mnemonics","title":"Hide Mnemonics","text":"

        When \"Hide Mnemonics\" mode is set to \"True\", your device will not display private key data or backup tools when a key is loaded. It will only show public key information and allow signing operations.

        "},{"location":"getting-started/settings/#appearance","title":"Appearance","text":"

        Configure screensaver time and change Krux to your desired theme.

        "},{"location":"getting-started/settings/#screensaver-time","title":"Screensaver time","text":"

        Set how long to wait idle before the screensaver appears. Enter 0 to disable the screensaver.

        "},{"location":"getting-started/settings/#theme","title":"Theme","text":"

        Choose your color theme according to your preference. Some themes may be more suitable for some devices, coordinator cameras and environments. As an example, it may be easier to scan QR codes from Krux devices using light theme in brighter environments.

        "},{"location":"getting-started/settings/#factory-settings","title":"Factory Settings","text":"

        Restore device to factory settings and reboot.

        "},{"location":"getting-started/features/QR-transcript-tools/","title":"Transcribing QR Codes","text":"

        When you export a mnemonic, encrypted mnemonic or a generic text QR code, alternative visualization modes will be available. Swipe left or right to change modes, or if your device doesn't have a touchscreen, press the PAGE buttons. Find transcribe templates here.

        "},{"location":"getting-started/features/QR-transcript-tools/#standard-mode","title":"Standard Mode","text":"

        This mode is optimized for scanning, the raw QR code will be displayed

        "},{"location":"getting-started/features/QR-transcript-tools/#lines-mode","title":"Lines Mode","text":"

        If you are good at transcribing things like handwritten text, with this mode one QR code line will be highlighted at a time. Press Enter to highlight the next line.

        "},{"location":"getting-started/features/QR-transcript-tools/#zoomed-regions-mode","title":"Zoomed Regions Mode","text":"

        QR codes will be split into regions, of 5x5 or 7x7 \"blocks\". One QR code region will be shown at a time. Press Enter to display the next region.

        "},{"location":"getting-started/features/QR-transcript-tools/#highlighted-regions-mode","title":"Highlighted Regions Mode","text":"

        QR codes will be split into regions, of 5x5 or 7x7 \"blocks\". One QR code region will be highlighted at a time. Press Enter to highlight the next region.

        "},{"location":"getting-started/features/QR-transcript-tools/#grided-mode","title":"Grided Mode","text":"

        Grids will be added to a standard QR code. In a dark room, if you place a sheet of paper over the device's screen, you'll notice QR code will be visible and it will be possible to copy it directly from above (tracing). Be careful not to damage your screen with pen and markers, use an insulating plastic tape or film to protect the device when using this method.

        "},{"location":"getting-started/features/encrypted-mnemonics/","title":"Encrypted Mnemonics","text":""},{"location":"getting-started/features/encrypted-mnemonics/#introduction","title":"Introduction","text":"

        There are many possible security layers one could add to protect a wallet\u2019s private key. Adding a BIP-39 passphrase to the mnemonic is the most common method. Encrypting a BIP-39 mnemonic has a similar use case as the BIP-39 passphrase, but the user experience may differ depending on the implementation. The main difference between BIP-39 passphrases and Krux\u2019s encrypted mnemonic implementation is that when users type the wrong key, encrypted mnemonics will return an error instead of loading a different wallet, as BIP-39 passphrases do. This difference may be desired or not. The implementation also has the convenience of storing a mnemonic ID together with the stored or QR code encrypted mnemonics. Mnemonic encryption, with its own key, can be used together with BIP-39 passphrase as an extra security layer.

        We use standard AES encryption modes ECB and CBC:

        "},{"location":"getting-started/features/encrypted-mnemonics/#aes-ecb","title":"AES-ECB","text":"

        ECB (Electronic Codebook) is a simpler method where encryption data blocks are encrypted individually. This mode is faster and simpler to encrypt, resulting in QR codes with lower density and easier to transcribe. It is generally considered less secure than CBC because it does not provide data chaining, meaning identical plaintext blocks will produce identical ciphertext blocks, making it vulnerable to pattern analysis. However, in Krux's implementation, only one or two binary data blocks are encrypted, so there will be no patterns, and the lack of chaining is not as relevant as it would be for larger files, plain text, or media.

        "},{"location":"getting-started/features/encrypted-mnemonics/#aes-cbc","title":"AES-CBC","text":"

        CBC (Cipher-block Chaining) is considered more secure. In the first data block, an initialization vector (IV) is used to add random data to the encryption. The encryption of subsequent blocks depends on the data from previous blocks, characterizing chaining. The tradeoff is that the encryption process will take longer because a snapshot will be needed to generate the IV. This IV will be stored together with encrypted data, making encrypted QR codes denser and harder to transcribe.

        "},{"location":"getting-started/features/encrypted-mnemonics/#cbc-encryption-iv","title":"CBC Encryption IV","text":"

        The Initial Vector (IV) will be generated from a snapshot taken with the camera. The IV is a fixed-size input value used in the first block of the encryption process. It adds randomness to the encryption, ensuring that data encrypted with the same key will produce different ciphertexts each time. The IV is not secret and will be transmitted along with the ciphertext. However, like any nonce, it should not be reused to maintain security.

        "},{"location":"getting-started/features/encrypted-mnemonics/#pbkdf2-iterations","title":"PBKDF2 Iterations","text":"

        When you enter the encryption key, it is not directly used to encrypt your data. In order to protect against brute force attacks, the key is derived multiple times using hashing functions. PBKDF2 (Password-Based Key Derivation Function) iterations refer to the number of derivations that will be performed over your key prior to encrypting/decrypting your mnemonic.

        "},{"location":"getting-started/features/encrypted-mnemonics/#encrypted-qr-codes-data-and-parsing","title":"Encrypted QR Codes Data and Parsing","text":"

        In search of efficiency and smaller QR codes, all data is converted to bytes and organized like a Bitcoin transaction, with variable and fixed length fields. The following data is present on the QR code:

        ID length (1) ID (2) Version (3) Key Derivations (4) IV (5) Encrypted Mnemonic (6) Validation Block (7) 1 Byte Variable 1 Byte 3 Bytes 16 Bytes (optional) 16 Bytes (12 words) 32 Bytes (24 words) 16 Bytes
        • Visible data (1 to 4):
          • (1) Mnemonic ID length (1 Byte).
          • (2) Mnemonic ID (variable lenght): Custom ID or wallet fingerprint.
          • (3) Version (1 Byte): Version of encryption method, currently two are available:
            • 0: AES-ECB-PBKDF2: Electronic Codebook with PBKDF2 key derivation.
            • 1: AES-CBC-PBKDF2: Cypher Block Chaining with PBKDF2 key derivation.
          • (4) Key derivation iterations (3 Bytes): Number of PBKDF2 key derivations times 10,000.
        • Cipher data (5 to 7):
          • (5) IV (16 Bytes-optional): Initial vector for AES-CBC encryption, possibility to be nonce for future AES-CTR or other encryption methods.
          • (6) Encrypted Mnemonic (16 Bytes - 12 words, 32 Bytes - 24 words): Mnemonic ciphertext.
          • (7) Validation block (16 Bytes): Currently using first 16 bytes of sha256 of the mnemonic bytes as checksum, could be used in future to store AES-AEX validation tag.
        "},{"location":"getting-started/features/encrypted-mnemonics/#considerations","title":"Considerations","text":"

        Storage of encrypted mnemonics on the device or SD cards are meant for convenience only and should not be considered a form of backup. Always make a physical backup of your keys that is independent from electronic devices and test recovering your wallet from this backup before you send funds to it.

        Remember that the stored encrypted mnemonic is protected by the key you defined to encrypt it. If the defined key is weak, your encrypted mnemonic will not be protected. If you have stored a mnemonic with funds in the device's internal flash memory using a weak key, the best way to undo this is to wipe the device.

        "},{"location":"getting-started/features/entropy/","title":"Empirical Entropy Measurement","text":""},{"location":"getting-started/features/entropy/#why-does-krux-say-the-entropy-of-my-fifty-dice-rolls-does-not-contain-128-bits-of-entropy","title":"Why Does Krux Say the Entropy of My Fifty Dice Rolls Does Not Contain 128 Bits of Entropy?","text":"

        This question, frequently raised in Krux chat groups, highlights the need to clarify the concepts and tools used by Krux to help users detect possible issues in the mnemonic creation procedure. Tools in Krux were designed to help users understand the concepts involved in the process, present statistics and indicators, and encourage users to experiment and evaluate results. This way, users learn about best practices in key generation. Below, we will dive deeper into entropy concepts to better support users in the fundamental requirement for sovereign self-custody, which is to build up knowledge.

        "},{"location":"getting-started/features/entropy/#entropy-in-dice-rolls","title":"Entropy in Dice Rolls","text":"

        Rolling dice and collecting the resulting values can be an effective method for generating cryptographic keys due to the inherent randomness and unpredictability of each roll. Each roll of a die produces a random number within a specific range, and when multiple rolls are combined, they create a sequence that is difficult to predict or reproduce. This sequence can be used to generate cryptographic keys that are robust against attacks. By ensuring that the dice rolls are conducted in a controlled and secure environment, and by using a sufficient number of rolls to achieve the desired level of randomness, one can create cryptographic keys that are highly secure and resistant to brute-force attacks or other forms of cryptanalysis.

        "},{"location":"getting-started/features/entropy/#entropy-definitions","title":"Entropy Definitions","text":"

        Entropy, a fundamental concept in various scientific disciplines, measures the degree of disorder or uncertainty within a system. This notion is interpreted differently across fields, leading to distinct types of entropy: mechanical entropy, Shannon's entropy, and cryptographic entropy.

        Mechanical entropy, rooted in thermodynamics and statistical mechanics, quantifies the disorder in a physical system. It describes how energy is distributed among the particles in a system, reflecting the system's tendency towards equilibrium and maximum disorder.

        Shannon's entropy, from information theory, measures the uncertainty or information content in a message or data source. Introduced by Claude Shannon, it quantifies the average amount of information produced by a stochastic source of data, indicating how unpredictable the data is.

        Cryptographic entropy, crucial in security, refers to the unpredictability and randomness required for secure cryptographic keys and processes. High cryptographic entropy ensures that keys are difficult to predict or reproduce, providing robustness against attacks.

        While mechanical entropy deals with physical systems, Shannon's entropy focuses on information content, and cryptographic entropy emphasizes security through randomness.

        "},{"location":"getting-started/features/entropy/#measuring-dice-rolls-entropy","title":"Measuring Dice Rolls Entropy","text":"

        Entropy is a theoretical measure and is not directly measurable from a single roll but rather from the probability distribution of outcomes over many rolls. We can use Shannon's formula for theoretical and empirical calculations. Entropy S can be quantified with:

        S = -\\sum_{i=1}^{n} p_i \\log(p_i)
        1. Empirical Measurement:
        2. Roll the dice a large number of times to observe the frequency of each outcome.
        3. Estimate the probabilities p_i based on observed frequencies.

        4. Theoretical Calculation:

        5. Use the uniform distribution assumption (equal probability for all outcomes).

        where: - p_i is the probability of each possible outcome (or state) of the system. - n is the number of possible outcomes.

        "},{"location":"getting-started/features/entropy/#empirical-real-vs-theoretical-entropy-in-dice-rolls","title":"Empirical (Real) vs. Theoretical Entropy in Dice Rolls","text":"

        When calculating the entropy of dice rolls, the difference between real and theoretical results arises from the assumption of perfect fairness and uniformity versus the inherent imperfections in real-world experiments.

        "},{"location":"getting-started/features/entropy/#theoretical-entropy","title":"Theoretical Entropy","text":"

        The theoretical entropy calculation assumes that the dice are perfectly fair, meaning each face has an equal probability of landing face up.

        Consider a fair six-sided die. The possible outcomes when rolling one die are {1, 2, 3, 4, 5, 6}, each with an equal probability of \\frac{1}{6}.

        1. Single Die Roll:
        2. Each outcome has a probability p_i = \\frac{1}{6}.
        3. The entropy S for one die roll is calculated as:
        S = - \\sum_{i=1}^{6} \\left( \\frac{1}{6} \\log_2 \\left( \\frac{1}{6} \\right) \\right)

        Since \\log_2(1/6) = -\\log_2(6) :

        S = -6 \\left( \\frac{1}{6} \\times -\\log_2(6) \\right) = \\log_2(6) \\approx 2.585 \\text{ bits}
        1. Multiple Dice Rolls:
        2. For multiple dice, the entropy increases as the number of possible outcomes increases. For k fair dice, the number of possible outcomes is 6^k.
        3. The entropy S for k dice is:

          S = \\log_2(6^k) = k \\log_2(6) \\approx 2.585k \\text{ bits}

        4. For example, entropy for the roll of 50 fair dice is calculated as:

          S = \\log_2(6^{50}) = 50 \\log_2(6) \\approx 2.585 \\times 50 \\approx 129.25 \\text{ bits}

        This calculation assumes that every outcome (each face of the die) has an equal likelihood, leading to a uniform distribution.

        "},{"location":"getting-started/features/entropy/#empirical-entropy","title":"Empirical Entropy","text":"

        In a real sample of dice rolls, several factors can cause deviations from the perfect uniform distribution:

        1. Imperfect Dice: Real dice may not be perfectly balanced. Small manufacturing defects can make certain faces slightly heavier or lighter, causing biases.
        2. Rolling Conditions: The way the dice are rolled, the surface they land on, and even air currents can introduce slight biases.
        3. Finite Sample Size: When rolling dice a finite number of times, the observed frequencies of each face will naturally deviate from the expected uniform distribution due to random variations. This phenomenon is more pronounced with smaller sample sizes.

        When you roll a die multiple times and observe the outcomes, you can calculate the empirical probabilities p_i of each face. Using these probabilities, the entropy is calculated as:

        S = - \\sum_{i=1}^{6} p_i \\log_2(p_i)"},{"location":"getting-started/features/entropy/#example","title":"Example","text":"

        Suppose you roll a six-sided die 50 times and get the following results:

        • 1: 4 times
        • 2: 9 times
        • 3: 7 times
        • 4: 10 times
        • 5: 12 times
        • 6: 8 times

        We can calculate Shannon's entropy as follows:

        "},{"location":"getting-started/features/entropy/#step-1-calculate-probabilities","title":"Step 1: Calculate Probabilities","text":"

        Total number of rolls:

        N = 4 + 9 + 7 + 10 + 12 + 8 = 50

        Probabilities for each outcome:

        p_1 = \\frac{4}{50} = 0.08 p_2 = \\frac{9}{50} = 0.18 p_3 = \\frac{7}{50} = 0.14 p_4 = \\frac{10}{50} = 0.2 p_5 = \\frac{12}{50} = 0.24 p_6 = \\frac{8}{50} = 0.16"},{"location":"getting-started/features/entropy/#step-2-compute-entropy","title":"Step 2: Compute Entropy","text":"

        Using Shannon's entropy formula:

        S = -\\sum_{i=1}^{n} p_i \\log_2(p_i)

        Calculate each term:

        S_1 = -p_1 \\log_2(p_1) = -0.08 \\log_2(0.08) = -0.08 \\times (-3.64386) = 0.291509 S_2 = -p_2 \\log_2(p_2) = -0.18 \\log_2(0.18) = -0.18 \\times (-2.47393) = 0.445307 S_3 = -p_3 \\log_2(p_3) = -0.14 \\log_2(0.14) = -0.14 \\times (-2.8365) = 0.39711 S_4 = -p_4 \\log_2(p_4) = -0.2 \\log_2(0.2) = -0.2 \\times (-2.32193) = 0.464386 S_5 = -p_5 \\log_2(p_5) = -0.24 \\log_2(0.24) = -0.24 \\times (-2.05889) = 0.494132 S_6 = -p_6 \\log_2(p_6) = -0.16 \\log_2(0.16) = -0.16 \\times (-2.64386) = 0.423018

        Sum the contributions:

        S = S_1 + S_2 + S_3 + S_4 + S_5 + S_6 S = 0.291509 + 0.445307 + 0.39711 + 0.464386 + 0.494132 + 0.423018 = 2.515462

        Thus, the Shannon's entropy for the given distribution of dice rolls is approximately 2.52 bits per roll.

        This will give you a different value than \\log_2(6) due to the deviations in the empirical probabilities.

        The total entropy for the N = 50 rolls is:

        S_{total} = S \\times N = 2.515 + 50 \\approx 125.8 \\text{ bits}"},{"location":"getting-started/features/entropy/#shannons-entropy-in-practice","title":"Shannon's Entropy in Practice","text":"

        Calculating Shannon's entropy on a real sample of dice rolls provides insights into the actual randomness and fairness of the dice and rolling conditions. Deviations from the theoretical entropy reflect the natural imperfections and variances inherent in real-world scenarios. This understanding helps in evaluating and improving the fairness and randomness of dice or similar systems.

        Shannon's entropy evaluates the statistical probability distribution of samples of a dice roll. An even distribution results in higher entropy, closer to the theoretical maximum entropy, which assumes perfectly distributed rolls. An uneven distribution, created, for example, by a biased die, will result in lower Shannon's entropy. In an extreme case, with a terribly biased die that always lands on the same side, Shannon's entropy will be zero.

        "},{"location":"getting-started/features/entropy/#cryptographic-entropy","title":"Cryptographic Entropy","text":"

        Shannon's entropy, while a powerful measure of information content and uncertainty in a statistical distribution for natural samples, is not considered cryptographic entropy due to its inability to detect patterns or other sources of predictability within data. Shannon's formula quantifies the average information produced by a stochastic process, essentially measuring the expected surprise in a sequence of symbols based on their probabilities. However, it does not account for potential structure, correlations, or regularities within the data that could be inserted by a user and exploited by an attacker.

        Cryptographic entropy, on the other hand, requires a higher standard of unpredictability. It must ensure that every bit of the cryptographic key is as random and independent as possible, making it resilient against any form of analysis that could reveal patterns or reduce the effective randomness. While Shannon's entropy can evaluate the statistical distribution of symbols, it falls short in guaranteeing the absence of patterns or dependencies, which are crucial for maintaining the security of cryptographic systems. Thus, cryptographic entropy encompasses a broader concept of randomness, ensuring that the generated keys are not only statistically random but also free from any detectable structure or predictability.

        "},{"location":"getting-started/features/entropy/#pattern-detection","title":"Pattern Detection","text":"

        It is possible to have dice rolls with an even distribution but poor cryptographic entropy. This issue arises when patterns are present in the sequences. Examples include sequences like 123456123456123..., 111122223333..., and 654321654321..., which exhibit poor cryptographic entropy despite having even distribution and high Shannon's entropy.

        To mitigate this issue, Krux has implemented a pattern detection algorithm that evaluates the Shannon's entropy of the rolls' derivatives. In practice, this algorithm detects arithmetic progression components in the dice rolls and raises a warning if a certain threshold is crossed.

        "},{"location":"getting-started/features/entropy/#what-krux-does","title":"What Krux Does?","text":"
        • Krux requires a minimum number of rolls based on theoretical entropy.
        • Krux warns the user if low Shannon's entropy, calculated with the actual rolls, is detected.
        • Krux warns the user if it suspects there are patterns within the actual rolls.
        "},{"location":"getting-started/features/entropy/#conclusion","title":"Conclusion","text":"

        While Krux cannot ensure rolls have good or bad cryptographic entropy, it does provide indicators to help users detect issues and learn about the concepts involved in mnemonic generation.

        "},{"location":"getting-started/features/printing/","title":"Printing","text":"

        Warning/Disclaimer: This feature is intended for individuals with experience in electronics tinkering and soldering.

        Krux has the ability to print mnemonic backup (Words, Numbers, Tiny Seed template; but not Stackbit 1248) and any QR code (SeedQR, signed PSBT, Address, XPUB, Wallet output descriptor, ...) via a locally-connected TTL serial thermal printer. Consult the parts list page for supported printers.

        Once a thermal printer and driver have been enabled in Krux settings, all screens that display a QR code will offer the option to Print to QR. Other formats of mnemonic backup will also ask if you want to Print to QR?.

        There are many ways you can use this functionality, including:

        • Printing backups of your mnemonics and multisig wallet output descriptor
        • Printing your xpubs and receive addresses to share
        • Printing signed messages and PSBTs

        Since printed thermal paper fades quickly, you can also print your backups on sticker thermal paper to use as templates for punching into more resilient materials like steel.

        We also have plans to add support for other kinds of QR \"printers\" in the future, including CNC machines. In this case, gcode will be generated that can be sent directly to a GRBL controller to cut your QRs out of wood or metal!

        Just be careful what you do with the printed codes, since most smartphones can now quickly and easily read QR codes. Treat your QR mnemonic the same way you would treat a plaintext copy of it.

        "},{"location":"getting-started/features/sd-card-update/","title":"SD Card Updates","text":""},{"location":"getting-started/features/sd-card-update/#upgrade-via-microsd-card","title":"Upgrade via microSD card","text":"

        Once you've installed the initial firmware on your device via USB, you can either continue updating the device by flashing via USB or you can perform upgrades via microSD card to keep the device airgapped.

        To perform an upgrade, simply copy the official release firmware.bin and firmware.bin.sig files to the root of a FAT-32 / MBR formatted microSD card, insert the card into your device, and reboot the device. If it detects the new firmware file and is able to verify the signature, you will be prompted to install it.

        Once installation is complete, eject the microSD card and delete the firmware files before reinserting and rebooting. Otherwise you will be prompted to install it again.

        We cannot guarantee that a microSD card is compatible and will work in your device; you'll need to test it on the device to be sure, read the FAQ for more info. Only official releases are signed and can be installed via microSD card.

        "},{"location":"getting-started/features/tools/","title":"Tools","text":"

        Here are some useful tools that are available as soon as Krux starts! These are offered as a complement to managing your device and wallets.

        "},{"location":"getting-started/features/tools/#check-sd-card","title":"Check SD Card","text":"

        You can check if a SD card can be detected and read by your device and explore its content. If there are too many files to fit on one screen, swipe up or down to navigate between the screens if your device has a touchscreen.

        "},{"location":"getting-started/features/tools/#print-test-qr","title":"Print Test QR","text":"

        Quickly print a test QR code to check and optimize your printer setup.

        "},{"location":"getting-started/features/tools/#create-qr-code","title":"Create QR Code","text":"

        Enter text to create, print or transcribe a QR code that can later be used as an encryption key or passphrase. Swipe left or right to change modes if your device has a touchscreen.

        "},{"location":"getting-started/features/tools/#descriptor-addresses","title":"Descriptor Addresses","text":"

        Verify if an address or list of addresses belong to a wallet without needing to load private keys. Simply load a trusted wallet descriptor from a QR code or SD card.

        "},{"location":"getting-started/features/tools/#remove-mnemonic","title":"Remove Mnemonic","text":"

        This option allows you to remove any stored encrypted mnemonic from the device's internal memory or an SD card. For more information, see Krux Encrypted Mnemonics.

        When mnemonics are removed from the device's flash memory, Krux will no longer be able to access them. However, as with most operating systems, the data may still be recoverable using specialized tools. If you stored any important keys with a weak encryption key, it is recommended to use the \"Wipe Device\" feature below to ensure that the data is irrecoverable.

        When mnemonics are removed from an SD card, Krux will overwrite the region where the encrypted mnemonic was stored with empty data. This makes it more secure to delete mnemonics from SD cards using Krux rather than a PC or another device. However, Krux does not have a \"Wipe\" feature for SD cards; you can find this feature in third-party applications.

        "},{"location":"getting-started/features/tools/#wipe-device","title":"Wipe Device","text":"

        This option permanently removes all stored encrypted mnemonics and settings from the device's internal flash memory. It ensures that the data is irrecoverable, making it an adequate measure to take if any important mnemonics were stored with a weak encryption key.

        "},{"location":"getting-started/installing/from-gui/","title":"From GUI application","text":"

        You can install Krux (both official or beta releases) onto your K210-based device using our official desktop application, KruxInstaller, available for Linux and Windows.

        "},{"location":"getting-started/installing/from-gui/#download","title":"Download","text":"

        Download the installer by choosing the right asset for your operating system from our Github releases page:

        Operational System File Windows krux-installer_0.0.13.exe* Debian-based: Ubuntu, PopOS, etc... krux-installer_0.0.13_amd64.deb* RedHat-based: Fedora, etc... krux-installer-0.0.13.x86_64.rpm* Any linux distribution krux-installer-0.0.13.AppImage* Package for Archlinux on AUR. krux-installer-bin"},{"location":"getting-started/installing/from-gui/#verify-files","title":"Verify files","text":"

        If you trust the project developers, you can skip to install:

        \u26a0\ufe0f TIP: This step helps detect if any unauthorized modifications were made between github and your local computer.

        "},{"location":"getting-started/installing/from-gui/#integrity","title":"Integrity","text":"System Commands Windows (powershell)
        # Compare this output:\n(Get-FileHash 'krux-installer_0.0.13.exe').Hash\n\n# With this:\nGet-Content 'krux-installer_0.0.13.exe.sha256.txt'\n
        Debian-based
        sha256sum --check ./krux-installer_0.0.13_amd64.deb.sha256.txt\n
        RedHat-based
        sha256txt --check ./krux-installer-0.0.13.x86_64.rpm.sha256.txt\n
        Any Linux distribution
        sha256sum --check ./krux-installer-0.0.13.AppImage.sha256.txt\n
        "},{"location":"getting-started/installing/from-gui/#authenticity","title":"Authenticity","text":"

        You will need have GPG installed. Most Linux already have this, but on Windows we recommend installing GPG4Win.

        Once installed, run this command to retrieve the developer's key:

        gpg --keyserver hkps://keys.openpgp.org --recv-keys B4281DDDFBBD207BFA4113138974C90299326322\n

        Then you can verify:

        System Command Windows (powershell) gpg --verify krux-installer_0.0.13.exe.sig Debian-based gpg --verify ./krux-installer_0.0.13_amd64.deb.sig RedHat-based gpg --verify ./krux-installer-0.0.13.x86_64.rpm.sig Any Linux distribution gpg --verify ./krux-installer-0.0.13.AppImage.sig

        \u26a0\ufe0f TIP: If the verification was successful, you may get a message similar to: Good signature from \"qlrddev <qlrddev@gmail.com>\"

        "},{"location":"getting-started/installing/from-gui/#install","title":"Install","text":"

        Each system require different steps to install:

        System Steps Windows
        • The krux-installer_0.0.13.exe is a NSIS installer;
        • The first time you run the .exe file the system will ask you to trust the application;
        • See windows section below for more information
        • Debian-based
          • Install with dpkg: sudo dpkg -i krux-installer_0.0.13_amd64.deb;
          • Update it with apt-get: sudo apt-get install -f krux-installer_0.0.13_amd64.deb.
          RedHat-based
          • Fedora: sudo dnf install krux-installer-0.0.13.x86_64.rpm;
          • Other RedHat based distros: sudo yum localinstall krux-installer-0.0.13.x86_64.rpm.
          Any Linux distribution
          • Place the krux-installer-0.0.13.AppImage where you want;
          • Modify permision to execute: chmod +x krux-installer-0.0.13.AppImage;
          • Run it: ./krux-installer-0.0.13.AppImage.
          "},{"location":"getting-started/installing/from-gui/#windows","title":"Windows","text":"

          On Windows, you'll be faced with a blue window saying \"Windows protected your PC\". This occurs because we don't have a code signing certificate, Click on \"More info\" and allow the execution. Follow the installer's instructions to complete the installation.

          \ud83d\udee1\ufe0f TIP: If you followed the steps presented in authenticity, you already have the assurance that the software is from a verified and genuine software publisher

          "},{"location":"getting-started/installing/from-gui/#usage","title":"Usage","text":""},{"location":"getting-started/installing/from-gui/#main-menu","title":"Main Menu","text":"

          When executing the Krux Installer, you will be presented with a menu of two items:

          "},{"location":"getting-started/installing/from-gui/#select-device","title":"Select Device","text":"

          First, select the device we want to flash. After that the menu will shown three items:

          "},{"location":"getting-started/installing/from-gui/#select-version","title":"Select Version","text":"

          This option allows you to select the latest official version or the latest beta version on our github. If the connection works, you will see the following screen:

          Version Name Advantages Disadvantages Official selfcustody/krux/releases/tag/*
          • Stable version;
          • All features were reviewd and tested;
          • It's integrity and authenticity can be verified.
            • Updates may take some time to be released.
            Beta odudex/krux_binaries
            • Latest features available;
            • Constant UX reviews by our community.
            • Its integrity or authenticity cannot be verified.
            "},{"location":"getting-started/installing/from-gui/#flash","title":"Flash","text":"

            Once you choose the device and version, it enables the \"flash with ktool\" button.

            \u26a0\ufe0f TIP: You must connect and turn on your device before flashing. In Linux, you'll be prompted to type your password (sudo or admin). Do not disconnect the device until the process is complete!

            When the flashing process completes, you should see the Krux logo:

            If it doesn't, try turning your device off and on by holding down the power button for six seconds.

            Congrats, you're now running Krux!

            "},{"location":"getting-started/installing/from-gui/#wipe-device","title":"Wipe device","text":"

            This option erases ALL data from the device's flash memory. On Linux, it will prompt for your password before. Once the process starts, the screen will appear frozen. When it's done, you can scroll down and see all the events that occurred.

            \u26a0\ufe0f TIP: It's useful when your device is not working or for security reasons. To use Krux again, you'll need to re-flash the firmware.

            \u26a0\ufe0f TIP: Do not unplug or poweroff your device or computer. Wait until the process finishes.

            "},{"location":"getting-started/installing/from-gui/#multilingual-support","title":"Multilingual support","text":"

            Prefer a different language? Krux has support for multiple languages. Once at the start screen, go to Settings, followed by Locale, and select the locale you wish to use.

            "},{"location":"getting-started/installing/from-gui/#upgrade-via-microsd-card","title":"Upgrade via microSD card","text":"

            Once you've installed the initial firmware on your device via USB, you can either continue updating the device by flashing or you can perform upgrades via microSD card to keep the device airgapped.

            "},{"location":"getting-started/installing/from-pre-built-release/","title":"From pre-built official release","text":"

            This page explains how to install Krux from an official, pre-built release.

            "},{"location":"getting-started/installing/from-pre-built-release/#download-the-latest-release","title":"Download the latest release","text":"

            Head over to the releases page and download the latest signed release.

            "},{"location":"getting-started/installing/from-pre-built-release/#verify-the-files","title":"Verify the files","text":"

            Before installing the release, it's a good idea to check that:

            1. The SHA256 hash of krux-vX.Y.Z.zip matches the hash in krux-vX.Y.Z.zip.sha256.txt
            2. The signature file krux-vX.Y.Z.zip.sig can be verified with the selfcustody.pem public key found in the root of the krux repository.

            You can either do this manually or with the krux shell script, which contains helper commands for this:

            ./krux sha256 krux-vX.Y.Z.zip\n./krux verify krux-vX.Y.Z.zip selfcustody.pem\n

            On Mac you may need to install coreutils to be able to use sha256sum

            brew install coreutils\n

            Fun fact: Each Krux release is signed with Krux!

            "},{"location":"getting-started/installing/from-pre-built-release/#flash-the-firmware-onto-the-device","title":"Flash the firmware onto the device","text":"

            Extract the latest version of Krux you downloaded and enter the folder:

            unzip krux-vX.Y.Z.zip && cd krux-vX.Y.Z\n

            Connect the device to your computer via USB (for Maix Amigo, make sure you\u2019re using bottom port), power it on, and run the following, replacing DEVICE with either m5stickv, amigo, bit, cube, dock or yahboom (to yahboom you may need to manually specify the port, for example /dev/ttyUSB0 on Linux or COM6 on Windows):

            ./ktool -B goE -b 1500000 maixpy_DEVICE/kboot.kfpkg\n

            For dock use the -B dan parameter:

            ./ktool -B dan -b 1500000 maixpy_dock/kboot.kfpkg\n

            When the flashing process completes, you should see the Krux logo:

            If it doesn't, try turning your device off and on by holding down the power button for six seconds.

            Congrats, you're now running Krux!

            "},{"location":"getting-started/installing/from-pre-built-release/#a-note-about-the-maix-amigo","title":"A note about the Maix Amigo","text":"

            Some Amigo screens have inverted X coordinates, others display colors differently. For more info see Troubleshooting.

            "},{"location":"getting-started/installing/from-pre-built-release/#troubleshooting","title":"Troubleshooting","text":"

            If ktool fails to run, you may need to give it executable permissions with chmod +x ./ktool, or you might need to use \"sudo\" if your user don't have access to serial port. In Windows or Mac you may need to explicitly allow the tool to run by adding an exception for it.

            If the flashing process fails midway through, check the connection, restart the device, and try the command again.

            Two serial ports are created when Amigo and Bit are connected to a PC. Sometimes Ktool will pick the wrong port and flashing will fail. Manually specify the serial port to overcome this issue using -p argument:

            "},{"location":"getting-started/installing/from-pre-built-release/#linux","title":"Linux","text":"

            See the correct port using ls /dev/ttyUSB*, in the example below we use /dev/ttyUSB0:

            ./ktool-linux -B goE -b 1500000 maixpy_amigo/kboot.kfpkg -p /dev/ttyUSB1\n

            "},{"location":"getting-started/installing/from-pre-built-release/#windows","title":"Windows","text":"

            See the correct port at Device Manager > Ports (COM & LPT), in the example below we use COM6:

            .\\ktool-win.exe -B goE -b 1500000 maixpy_amigo\\kboot.kfpkg -p COM6\n

            "},{"location":"getting-started/installing/from-pre-built-release/#mac","title":"Mac","text":"

            Remove the Gatekeeper quarantine extended attribute from ktool-mac:

            xattr -d com.apple.quarantine ktool-mac\n

            See the correct port using the command line: ls /dev/cu.usbserial*, in the example below we use /dev/cu.usbserial-10 (If the output isn't what you expect try a different cable, preferably a smartphone usb-c charger cable):

            ./ktool-mac -B goE -b 1500000 maixpy_amigo/kboot.kfpkg -p /dev/cu.usbserial-10\n

            Different OS versions may have different port names, and the absence of ports may indicate a connection, driver or hardware related issue. See Troubleshooting for more info.

            "},{"location":"getting-started/installing/from-pre-built-release/#multilingual-support","title":"Multilingual support","text":"

            Prefer a different language? Krux has support for multiple languages. Once at the start screen, go to Settings, followed by Locale, and select the locale you wish to use.

            "},{"location":"getting-started/installing/from-pre-built-release/#upgrade-via-microsd-card","title":"Upgrade via microSD card","text":"

            Once you've installed the initial firmware on your device via USB, you can either continue updating the device by flashing or you can perform upgrades via microSD card to keep the device airgapped.

            "},{"location":"getting-started/installing/from-source/","title":"From source","text":"

            This page explains how to install Krux from source. You can check a simplified version of these instructions in our README too.

            "},{"location":"getting-started/installing/from-source/#fetch-the-code","title":"Fetch the code","text":"

            This will download the source code of Krux as well as the code of all its dependencies inside a new folder called krux (needs git):

            git clone --recurse-submodules https://github.com/selfcustody/krux\n

            Note: When you wish to pull updates (to all submodules, their submodules, ...) to this repo, use:

            git pull origin main && git submodule update --init --recursive\n

            "},{"location":"getting-started/installing/from-source/#prerequisite-for-upgrading-via-microsd","title":"Prerequisite for upgrading via microSD","text":"

            If you wish to perform airgapped upgrades via microSD card later, you will need to have a private and public key pair to sign your builds and verify the signatures. If you do not want to perform further airgapped upgrades, jump to build section.

            You can use an existing Krux installation and mnemonic to sign your builds with, or you can generate a keypair and sign from the openssl CLI. Commands have been added to the krux shell script to make this easier.

            In either case, you will need to update the SIGNER_PUBKEY field in src/krux/metadata.py to store your public key so that Krux can verify future builds before installing.

            To generate a keypair:

            ./krux generate-keypair\n./krux pem-to-pubkey pubkey.pem\n

            The first command will create privkey.pem and pubkey.pem files you can use with openssl, and the second command will output your public key in the form expected by Krux.

            Once you've updated the SIGNER_PUBKEY with this value, you can proceed with the regular build process.

            "},{"location":"getting-started/installing/from-source/#build-the-firmware-linux-or-wsl","title":"Build the firmware (Linux or WSL)","text":"

            The krux bash script contains commands for common development tasks. It assumes a Linux host, you will need to have Docker Desktop or Docker Engine, openssl, and wget installed at a minimum for the commands to work as expected. It works on Windows using WSL. The channel Crypto Guide from Youtube made a step-by-step video - Krux DIY Bitcoin Signer: Build From Source & Verify (With Windows + WSL2 + Docker)

            To build and flash the firmware:

            # build firmware for Maix Amigo\n./krux build maixpy_amigo\n

            The first time, the build can take around an hour or so to complete. Subsequent builds should take only a few minutes. If all goes well, you should see a new build folder containing firmware.bin and kboot.kfpkg files when the build completes.

            Note: if you encounter any of these errors while building, it is a problem connecting to github, try again (if the error persists, try changing the DNS/VPN or correcting the hostname resolution of github.com to an IP that is working for you):

            error: RPC failed; curl 92 HTTP/2 stream 0 was not closed cleanly: CANCEL (err8)\nfatal: the remote end hung up unexpectedly\nfatal: early EOF\nfatal: index-pack failed\nfatal: clone of ... failed\nFailed to clone ...\n

            "},{"location":"getting-started/installing/from-source/#reproducibility","title":"Reproducibility","text":"

            If you build from the main branch of the source code, you should be able to reproduce the build process used to generate the last release binaries and obtain exact copies of the firmware.bin and kboot.kfpkg files, with matching hash checksums.

            To extract and verify the firmware.bin contained in kboot.kfpkg, you can use the following command:

            unzip kboot.kfpkg -d ./kboot/\n
            "},{"location":"getting-started/installing/from-source/#flash-the-firmware-onto-the-device","title":"Flash the firmware onto the device","text":"

            Connect the device to your computer via USB (for Maix Amigo, make sure you\u2019re using bottom port), power it on, and run the following, replacing DEVICE with either m5stickv, amigo, bit, cube, dock or yahboom:

            # build firmware for DEVICE\n./krux flash maixpy_DEVICE\n
            If the flashing fails try one of the following common solutions listed on FAQ

            When the flashing process completes, you should see the Krux logo:

            If it doesn't, try turning your device off and on by holding down the power button for six seconds.

            Congrats, you're now running Krux!

            "},{"location":"getting-started/installing/from-source/#a-note-about-the-maix-amigo","title":"A note about the Maix Amigo","text":"

            Some Amigo screens have inverted X coordinates, others display colors differently. For more info see Troubleshooting.

            "},{"location":"getting-started/installing/from-source/#signing-the-firmware","title":"Signing the firmware","text":"

            You can sign the firmware to perform airgapped upgrades using one of the two methods listed below:

            "},{"location":"getting-started/installing/from-source/#method-1-signing-from-krux","title":"Method 1: Signing from Krux","text":"

            First, calculate the SHA256 hash of the new firmware by running:

            ./krux sha256 build/firmware.bin\n

            Copy this hex string and turn it into a QR code using whichever QR code generator you'd like.

            In Krux, enter the mnemonic of your private key that will be used for signing, and go to Sign > Message. Scan the QR code you generated, and you will be asked if you wish to sign the hash. Proceed, and you will be presented with a base64-encoded string containing the signature, as text and as a QR code.

            Take this string and create a signature file by running:

            ./krux b64decode \"signature-in-base64\" > build/firmware.bin.sig\n

            This will generate a firmware.bin.sig file containing a signature of the firmware's SHA256 hash.

            "},{"location":"getting-started/installing/from-source/#method-2-signing-from-your-computer-with-openssl","title":"Method 2: Signing from your computer with OpenSSL","text":"

            With the keypair you generated before, you can now run:

            ./krux sign build/firmware.bin privkey.pem\n

            This will generate a firmware.bin.sig file containing a signature of the firmware's SHA256 hash.

            "},{"location":"getting-started/installing/from-test-release/","title":"From pre-built test release","text":"

            This page explains how to install Krux from a test (beta), pre-built release.

            "},{"location":"getting-started/installing/from-test-release/#warning","title":"Warning","text":"

            Keep in mind that these are unsigned binaries.

            "},{"location":"getting-started/installing/from-test-release/#download","title":"Download","text":"

            Download experimental compiled firmware or the Android app apk from our test (beta) repository.

            "},{"location":"getting-started/installing/from-test-release/#android","title":"Android","text":"

            The Krux Android app is designed for learning about Krux and Bitcoin air-gapped transactions. Due to the numerous potential vulnerabilities inherent in smartphones, such as the lack of control over the operating system, libraries, and hardware peripherals, the Krux app should NOT be used to manage wallets containing savings or important keys and mnemonics. For secure management of your keys, a dedicated device is recommended. More

            "},{"location":"getting-started/installing/from-test-release/#compiled-firmware-for-kendryte-k210-devices","title":"Compiled firmware for Kendryte K210 devices","text":""},{"location":"getting-started/installing/from-test-release/#m5stickv","title":"M5StickV","text":"

            To Flash M5StickV run the following.

            "},{"location":"getting-started/installing/from-test-release/#linux","title":"Linux","text":"
            ./ktool-linux -B goE -b 1500000 maixpy_m5stickv/kboot.kfpkg\n
            "},{"location":"getting-started/installing/from-test-release/#mac","title":"Mac","text":"
            ./ktool-mac -B goE -b 1500000 maixpy_m5stickv/kboot.kfpkg\n
            "},{"location":"getting-started/installing/from-test-release/#windows","title":"Windows","text":"
            .\\ktool-win.exe -B goE -b 1500000 maixpy_m5stickv\\kboot.kfpkg\n
            "},{"location":"getting-started/installing/from-test-release/#sipeed-maix-amigo","title":"Sipeed Maix Amigo","text":"

            To Flash Maix Amigo run the following.

            "},{"location":"getting-started/installing/from-test-release/#linux_1","title":"Linux","text":"
            ./ktool-linux -B goE -b 1500000 maixpy_amigo/kboot.kfpkg\n
            "},{"location":"getting-started/installing/from-test-release/#mac_1","title":"Mac","text":"
            ./ktool-mac -B goE -b 1500000 maixpy_amigo/kboot.kfpkg\n
            "},{"location":"getting-started/installing/from-test-release/#windows_1","title":"Windows","text":"
            .\\ktool-win.exe -B goE -b 1500000 maixpy_amigo\\kboot.kfpkg\n
            "},{"location":"getting-started/installing/from-test-release/#a-note-about-the-maix-amigo","title":"A note about the Maix Amigo","text":"

            Some Amigo screens have inverted X coordinates, others display colors differently. For more info see Troubleshooting.

            "},{"location":"getting-started/installing/from-test-release/#sipeed-maix-bit","title":"Sipeed Maix Bit","text":"

            To Flash Maix Bit run the following.

            "},{"location":"getting-started/installing/from-test-release/#linux_2","title":"Linux","text":"
            ./ktool-linux -B goE -b 1500000 maixpy_bit/kboot.kfpkg\n
            "},{"location":"getting-started/installing/from-test-release/#mac_2","title":"Mac","text":"
            ./ktool-mac -B goE -b 1500000 maixpy_bit/kboot.kfpkg\n
            "},{"location":"getting-started/installing/from-test-release/#windows_2","title":"Windows","text":"
            .\\ktool-win.exe -B goE -b 1500000 maixpy_bit\\kboot.kfpkg\n
            "},{"location":"getting-started/installing/from-test-release/#sipeed-maix-cube","title":"Sipeed Maix Cube","text":"

            To Flash Maix Cube run the following.

            "},{"location":"getting-started/installing/from-test-release/#linux_3","title":"Linux","text":"
            ./ktool-linux -B goE -b 1500000 maixpy_cube/kboot.kfpkg\n
            "},{"location":"getting-started/installing/from-test-release/#mac_3","title":"Mac","text":"
            ./ktool-mac -B goE -b 1500000 maixpy_cube/kboot.kfpkg\n
            "},{"location":"getting-started/installing/from-test-release/#windows_3","title":"Windows","text":"
            .\\ktool-win.exe -B goE -b 1500000 maixpy_cube\\kboot.kfpkg\n
            "},{"location":"getting-started/installing/from-test-release/#sipeed-maix-dock","title":"Sipeed Maix Dock","text":"

            To Flash Maix Dock you need to pass the -B dan parameter.

            "},{"location":"getting-started/installing/from-test-release/#linux_4","title":"Linux","text":"
            ./ktool-linux -B dan -b 1500000 maixpy_dock/kboot.kfpkg\n
            "},{"location":"getting-started/installing/from-test-release/#mac_4","title":"Mac","text":"
            ./ktool-mac -B dan -b 1500000 maixpy_dock/kboot.kfpkg\n
            "},{"location":"getting-started/installing/from-test-release/#windows_4","title":"Windows","text":"
            .\\ktool-win.exe -B dan -b 1500000 maixpy_dock\\kboot.kfpkg\n
            "},{"location":"getting-started/installing/from-test-release/#aimotion-yahboom-k210-module","title":"Aimotion Yahboom k210 module","text":"

            To Flash Yahboom k210 module you'll have to manually specify the port.

            "},{"location":"getting-started/installing/from-test-release/#linux_5","title":"Linux","text":"

            See the correct port using ls /dev/ttyUSB*, in the example below we use /dev/ttyUSB0:

            ./ktool-linux -B goE -b 1500000 -p /dev/ttyUSB0 yahboom/kboot.kfpkg\n

            "},{"location":"getting-started/installing/from-test-release/#mac_5","title":"Mac","text":"

            See the correct port using the command line: ls /dev/cu.usbserial*, in the example below we use /dev/cu.usbserial-10:

            ./ktool-mac -B goE -b 1500000 -p /dev/cu.usbserial-10 yahboom/kboot.kfpkg\n

            "},{"location":"getting-started/installing/from-test-release/#windows_5","title":"Windows","text":"

            See the correct port at Device Manager > Ports (COM & LPT), in the example below we use COM6:

            .\\ktool-win.exe -B goE -b 1500000 -p COM6 yahboom\\kboot.kfpkg\n

            "},{"location":"getting-started/installing/","title":"Installing","text":"

            You can install Krux in four different ways:

            • From GUI application
            • From pre-built official release
            • From pre-built test (beta) release
            • From source
            "},{"location":"getting-started/installing/#requirements","title":"Requirements","text":"

            Please, check the parts list for the compatible devices and requirements.

            After the first firmware install, you can use a microSD card if you wish to perform further airgapped updates.

            "},{"location":"getting-started/usage/generating-a-mnemonic/","title":"Generating a Mnemonic","text":"

            Krux supports creating 12 and 24-word BIP-39 mnemonic seed phrases. Since generating true entropy is challenging, especially with an embedded device, we recommend outsourcing entropy generation using dice rolls. However, it is also possible to randomly pick words (e.g., SeedPicker) or use the camera as a source of entropy to quickly create a mnemonic.

            At the start screen, after selecting New Mnemonic, you will be taken to a second menu where you can choose to create a mnemonic via the camera, words, rolls of a D6 (standard six-sided die), or a D20 (20-sided die).

            "},{"location":"getting-started/usage/generating-a-mnemonic/#camera","title":"Camera","text":"

            (Experimental!) Choose between 12 or 24 words, then take a random picture and Krux will generate a mnemonic from the hash of the image bytes.

            "},{"location":"getting-started/usage/generating-a-mnemonic/#image-entropy-quality-estimation","title":"Image Entropy Quality Estimation","text":"

            During image capture, entropy quality estimation is displayed to assist you in obtaining a high-quality image source for your key. After a snapshot is taken, Shannon's entropy and pixel deviation indices are presented. Minimum thresholds are established to prevent the use of poor-quality images with low entropy for key generation. It's important to note that these values serve as indicators or estimations of entropy quality, but they are not absolute entropy values in a cryptographic context.

            "},{"location":"getting-started/usage/generating-a-mnemonic/#words","title":"Words","text":"

            Print the BIP39 word list in 3D or on paper, then cut out the words and place them in a bucket. Manually draw 11 or 23 words from the bucket. For the final word, Krux will assist you in picking a valid 12th or 24th word by adjusting its smart keypad to only allow typing words with a valid checksum. Alternatively, you can leave it empty, and Krux will select a final, valid checksum word for you.

            "},{"location":"getting-started/usage/generating-a-mnemonic/#dice-rolls","title":"Dice Rolls","text":""},{"location":"getting-started/usage/generating-a-mnemonic/#via-d6","title":"Via D6","text":"

            Choose between 12 or 24 words.

            The entropy in a single roll of a D6 is 2.585 bits ( log2(6) ); therefore a minimum of a 50 rolls will be required for 128 bits of entropy, enough to generate a 12-word mnemonic. For 24 words, or an entropy of 256 bits, a minimum of 99 rolls will be required.

            "},{"location":"getting-started/usage/generating-a-mnemonic/#via-d20","title":"Via D20","text":"

            Since a D20 has more possible outcomes, the entropy is increased per roll to 4.322 bits ( log2(20) ). This means that only 30 rolls are necessary to create a 12-word mnemonic and 60 rolls for a 24-word mnemonic.

            "},{"location":"getting-started/usage/generating-a-mnemonic/#dice-rolls-entropy-quality-estimation","title":"Dice Rolls Entropy Quality Estimation","text":"

            When you input your dice rolls, you'll see two progress bars filling up. The top progress bar shows how many rolls you've entered compared to the minimum number needed. The bottom progress bar shows the real-time calculated Shannon's entropy compared to the required minimum (128 bits for 12 words and 256 bits for 24 words). When the Shannon's entropy estimation reaches the recommended level, the progress bar will be full, and its frame will change color. If you've met the minimum number of rolls but the entropy estimation is still below the recommended level, a warning will appear, suggesting you add more rolls to increase entropy. Note: Similar to image entropy quality estimation, dice rolls Shannon's entropy serves as an indicator and should not be considered an absolute measure of cryptographic entropy.

            Learn more about Krux Entropy Quality Estimation

            "},{"location":"getting-started/usage/generating-a-mnemonic/#stats-for-nerds","title":"Stats for Nerds","text":"

            A low Shannon's entropy value might suggest that your dice are biased or that there's a problem with how you're gathering entropy. To investigate further, examine the \"Stats for Nerds\" section to check the distribution of your rolls and look for any abnormalities.

            "},{"location":"getting-started/usage/generating-a-mnemonic/#how-it-works","title":"How it works","text":"

            For dice rolls, Krux keeps track of every roll you enter and displays the cumulative string of outcomes after each roll.

            When you have entered your final roll, Krux will hash this string using SHA256 and output the resulting hash to the screen so that you can verify it for yourself.

            In the case a camera snapshot is used as source, image bytes, which contain pixels data in RGB565 format, will be hashed just like it is done with the dice rolls string.

            Krux then takes this hash, runs unhexlify on it to encode it as bytes, and deterministically converts it into a mnemonic according to the BIP-39 Reference Implementation.

            Note: For 12-word mnemonics, only the first half of the SHA256 hash is used (128 bits), while 24-word mnemonics use the full hash (256 bits).

            "},{"location":"getting-started/usage/generating-a-mnemonic/#how-to-verify","title":"How to verify","text":"

            Don't trust, verify. We encourage you not to trust any claim you cannot verify yourself. Therefore, there are wallets that use compatible algorithms to calculate the entropy derived from dice rolls. You can use the SeedSigner or Coldcard hardware wallets, or even the Bitcoiner Guide website, they share the same logic that Krux uses and will give the same mnemonic for the dice roll method.

            "},{"location":"getting-started/usage/loading-a-mnemonic/","title":"Loading a Mnemonic","text":"

            Once you have either a 12- or 24-word BIP-39 mnemonic, choose Load Mnemonic on Krux's start menu, and you will be presented with several input methods:

            "},{"location":"getting-started/usage/loading-a-mnemonic/#input-methods","title":"Input Methods","text":""},{"location":"getting-started/usage/loading-a-mnemonic/#via-camera","title":"Via Camera","text":"

            You can choose to use the camera to scan a QR code or Tiny Seed metal plate backup.

            If you are in a dark environment, you can hold down the ENTER button of the M5StickV or Maix Amigo to turn on their LED light to potentially increase visibility. Some cameras (OV7740 and OV2640) have an anti-glare mode to better capture images from high brightness screens or with incident light, they are present on M5StickV, Amigo, Cube and some Yahboom. To enable/disable the anti-glare mode on a supported device just press the PAGE button while scanning.

            "},{"location":"getting-started/usage/loading-a-mnemonic/#qr-code","title":"QR Code","text":"

            It's unpleasant having to manually enter 12 or 24 words every time you want to use Krux. To remedy this you can instead use the device's camera to read a QR code containing the words. Krux will decode QR codes of four types:

            • Plain text QR: The mnemonic words encoded as text, with words separated by spaces.
            • SeedQR: Basically, it is the mnemonic words of the respective BIP-39 numbers concatenated, encoded as text.
            • Compact SeedQR: Basically, it is the mnemonic words bits concatenated as bytes.
            • Encrypted Mnemonic: A specification created by Krux that encrypts the mnemonic words bits and adds some information about the encryption used.

            After opening your wallet via one of the manual methods you can use Krux to create QR codes of all types above, transcript them to paper or metal using the transcription helpers or attach a thermal printer to your Krux and print out the mnemonic. Check out the Printing section for more information. You can also use an offline QR code generator for this (ideally on an airgapped device).

            "},{"location":"getting-started/usage/loading-a-mnemonic/#tiny-seed","title":"Tiny Seed","text":"

            Tiny Seed is a compact metal plate mnemonic backup method. Krux devices have machine vision capabilities that allow users to scan these metal plates and instantly load mnemonics engraved on them. To properly scan them place the Tiny Seed over a black background and paint the punched bits black to increase contrast. You can also scan the thermally printed version, or a filled template. Find templates to scan or print here.

            "},{"location":"getting-started/usage/loading-a-mnemonic/#via-manual-input","title":"Via Manual Input","text":"

            Manually type Words, Word Numbers, Tiny Seed (toggle the bits or punches) or Stackbit (model 1248 metal plate backup).

            "},{"location":"getting-started/usage/loading-a-mnemonic/#words","title":"Words","text":"

            Enter each word of your BIP-39 mnemonic one at a time. Krux will disable impossible-to-reach letters as you type and will attempt to autocomplete your words to speed up the process.

            "},{"location":"getting-started/usage/loading-a-mnemonic/#word-numbers","title":"Word Numbers","text":""},{"location":"getting-started/usage/loading-a-mnemonic/#decimal","title":"Decimal","text":"

            Enter each word of your BIP-39 mnemonic as a number (1-2048) one at a time. You can use this list for reference.

            "},{"location":"getting-started/usage/loading-a-mnemonic/#hexadecimal-and-octal","title":"Hexadecimal and Octal","text":"

            You can also enter your BIP-39 mnemonic word's numbers (1-2048) in hexadecimal format, with values ranging from 0x1 to 0x800, or in octal format, with values ranging from 01 to 04000. This is useful with some metal plate backups that uses those formats.

            "},{"location":"getting-started/usage/loading-a-mnemonic/#tiny-seed-bits","title":"Tiny Seed (Bits)","text":"

            Enter the BIP-39 mnemonic word's numbers (1-2048) in binary format, toggling necessary bits to recreate each of the word's respective number. The last word will have checksum bits dynamically toggled while you fill the bits.

            "},{"location":"getting-started/usage/loading-a-mnemonic/#stackbit-1248","title":"Stackbit 1248","text":"

            Enter the BIP-39 mnemonic word's numbers (1-2048) using the Stackbit 1248 metal plate backup method, where each of the four digits of the word's number is a sum of the numbers marked (punched) 1, 2, 4, or 8. For example, to enter the word \"pear\", number 1297, you must punch (1)(2)(1+8=9)(1+2+4=7).

            "},{"location":"getting-started/usage/loading-a-mnemonic/#from-storage","title":"From Storage","text":"

            You can retrieve mnemonics previously stored on device's internal flash or external (SD card). All stored mnemonics are encrypted, to load them you'll have to enter the same key you used to encrypt them.

            "},{"location":"getting-started/usage/loading-a-mnemonic/#wallet-loading","title":"Wallet Loading","text":""},{"location":"getting-started/usage/loading-a-mnemonic/#confirm-mnemonic-words","title":"Confirm Mnemonic Words","text":"

            Once you have entered your mnemonic, you will be presented with the full list of words to confirm.

            "},{"location":"getting-started/usage/loading-a-mnemonic/#confirm-wallet-attributes","title":"Confirm Wallet Attributes","text":"

            You will be presented with a screen containing wallet attributes, if they are as expected just press Load Wallet and you'll be ready to use your loaded key.

            "},{"location":"getting-started/usage/loading-a-mnemonic/#attributes","title":"Attributes","text":"

            Fingerprint

            73c5da0a

            The BIP-32 master wallet's fingerprint, if you have it noted down, will help you make sure you entered the correct mnemonic and passphrase (optional) and will load the expected wallet.

            Network

            Mainnet

            Check if you are loading a Testnet or Mainnet wallet.

            Single/Multisig

            Single-sig

            Check if you are loading a Single-sig or Multisig wallet.

            Derivation Path

            m/84'/0'/0'

            The derivation path is a sequence of numbers, or \"nodes\", that define the script type, network, and account index of your wallet.

            1. Script Type: The first number defines the script type. The default is 84', corresponding to a Native Segwit wallet. Other values include:

              44' for Legacy

              49' for Nested Segwit

              86' for Taproot

              48' for Multisig

            2. Network: The second number defines the network:

              0' for Mainnet

              1' for Testnet

            3. Account Index: The third number is the account index, with 0' being the default.

            For multisig wallets, a fourth node with the value 2' is added to the derivation path.

            Passphrase

            No Passphrase

            Informs if the wallet has a loaded passphrase.

            "},{"location":"getting-started/usage/loading-a-mnemonic/#changing-wallet-attributes","title":"Changing Wallet Attributes","text":"

            You can change any of the attributes before and after loading a wallet. It is also possible to change default settings for Network and Single/Multisig on settings.

            "},{"location":"getting-started/usage/loading-a-mnemonic/#passphrase","title":"Passphrase","text":"

            You can type or scan a BIP-39 passphrase. When typing, swipe left or right to change keypads if your device has a touchscreen. For scanning, you can also create a QR code from your offline passphrase in Tools.

            "},{"location":"getting-started/usage/loading-a-mnemonic/#customize","title":"Customize","text":"

            Press Customize to open a menu where you can change the Network, Single/Multisig, Script Type and Account.

            Now, onto the main menu...

            "},{"location":"getting-started/usage/navigating-the-main-menu/","title":"Navigating the Main Menu","text":"

            After entering your mnemonic, and loading a wallet, you will find yourself on Krux's main menu. Below is a breakdown of the entries available:

            "},{"location":"getting-started/usage/navigating-the-main-menu/#backup-mnemonic","title":"Backup Mnemonic","text":"

            This will open a new submenu with different types of backups. QR Code based, Encrypted and Other Formats

            If you set a printer, it will also give the option to print them!

            "},{"location":"getting-started/usage/navigating-the-main-menu/#qr-code","title":"QR Code","text":"
            • Plaintext QR

            Generate a QR containing the mnemonic words as regular text, where words are separated by spaces. Any QR code can be printed if a thermal printer driver is set.

            • Compact SeedQR

            A QR code is created from a binary representation of mnemonic words. Format created by SeedSigner, more info here.

            • SeedQR

            Words are converted to their BIP-39 numeric indexes, those numbers are then concatenated as a string and finally converted to a QR code. Format created by SeedSigner, more info here.

            • Encrypted QR Code

            This option converts the encrypted mnemonic into a QR code. Enter an encryption key and, optionally, a custom ID. When you scan this QR code through \"Load Mnemonic\" -> \"Via Camera\" -> \"QR Code,\" you will be prompted to enter the decryption key to load the mnemonic stored in it. Like any QR code, it can be printed if a thermal printer driver is set up.

            "},{"location":"getting-started/usage/navigating-the-main-menu/#encrypted","title":"Encrypted","text":"

            This feature allows you to back up your mnemonic by encrypting it and storing it on the device's flash memory, an SD card, or in QR code format. You can customize the encryption method and parameters in the settings.

            For convenience, you may choose to store the encrypted mnemonic on flash memory or an SD card, but it is advisable not to rely solely on these methods for backup. Flash storage can degrade over time and may be subject to permanent damage, resulting in the loss of stored information.

            When using any of the encryption methods, you will be prompted to enter an encryption key. This key can be provided in text or QR code format. Additionally, you have the option to set a custom ID for easier management of your mnemonics. If a custom key is not specified, the device's current loaded wallet fingerprint will be used as the ID.

            See this page to find out more about: Krux Mnemonics Encryption.

            • Store on Flash

            This option stores the encrypted mnemonic in the device's flash memory. You can decrypt and load it later through the \"Load Mnemonic\" -> \"From Storage\" option.

            • Store on SD Card

            If an SD card is available, this option stores the encrypted mnemonic on it. You can decrypt and load it later through the \"Load Mnemonic\" -> \"From Storage\" option.

            • Encrypted QR Code It's another path for the same functionality present on QR Code backups, described above.
            "},{"location":"getting-started/usage/navigating-the-main-menu/#other-formats","title":"Other Formats","text":"
            • Words

            Display the BIP-39 mnemonic words as text so you can write them down.

            • Numbers

            Display the BIP-39 mnemonic word numbers (1-2048) in decimal, hex, or octal format.

            • Stackbit 1248

            This metal backup format represents the BIP-39 mnemonic word's numbers (1-2048). Each of the four digits is converted to a sum of 1, 2, 4 or 8. This option does not print even if a printer driver is set.

            • Tiny Seed

            This metal backup format represents the BIP-39 mnemonic word's numbers (1-2048) in binary format on a metal plate, where the 1's are marked (punched) and the 0's are left intact. You can also print your mnemonic in this format if a thermal printer driver is set.

            "},{"location":"getting-started/usage/navigating-the-main-menu/#extended-public-key","title":"Extended Public Key","text":"

            A menu will be presented with options to display your master extended public key (xPub) as text and as a QR code. Depending on the script type or whether a single-sig or multisig wallet was loaded, the options shown will be xPub, yPub, zPub, or ZPub. When displayed as text, the extended public key can be stored on an SD card if available. If you choose to export a QR code, you can not only scan it but also save it as an image on an SD card or print it if a thermal printer is attached.

            All QR codes will contain key origin information in key expressions. If your coordinator cannot parse this information, it will not be capable of importing the wallet's fingerprint. As a result, Krux will not perform important verifications when signing transactions created by it unless you manually add the fingerprint so that it can be used to create Krux-compatible PSBTs.

            Always prefer to import extended public keys directly from Krux when setting up a coordinator instead of copying it (or parts of it) from other sources.

            "},{"location":"getting-started/usage/navigating-the-main-menu/#wallet","title":"Wallet","text":"

            Here you can load view and save wallet descriptors, add or change passphrases, customize wallet's attributes, generate and load a BIP85 child mnemonic.

            "},{"location":"getting-started/usage/navigating-the-main-menu/#wallet-descriptor","title":"Wallet Descriptor","text":"

            A Bitcoin Wallet Output Script Descriptor defines a set of addresses in a wallet. It includes the following information:

            • Script Type: Specifies the type of script (e.g., P2PKH, P2SH, P2WPKH).

            • Origin Info: Defines the master fingerprint and derivation path used to derive keys.

            • Extended Public Keys: usually represented as an xpub, but could be ypub, zpub, etc.

            Output descriptors standardize how wallets generate addresses, ensuring compatibility and security. They help wallets and other software understand how to derive and verify the addresses used in transactions.

            For multisig wallets, it is essential to load a descriptor to check addresses and perform full PSBT verification. For single-sig wallets, loading a descriptor is optional and serves as a redundancy check of the coordinator's wallet attributes.

            When you select the \"Wallet Descriptor\" option for the first time, you will be prompted to load a wallet descriptor via QR code or SD card. After loading, a preview of the wallet attributes will be displayed for confirmation.

            If you access the \"Wallet Descriptor\" option again after loading your wallet, you will see the wallet's name, fingerprints, and the abbreviated XPUBs of all cosigners, along with a QR code containing the exact data that was initially loaded. If an SD card is inserted, you can save the descriptor to it for later use without the assistance of a coordinator. Additionally, if you have a thermal printer attached, you can print this QR code.

            Krux also allows you to verify a descriptor's receive and change addresses without the need to load private keys. Simply turn on your Krux, access \"Tools\" -> \"Descriptor Addresses,\" and load a trusted descriptor from a QR code or SD card.

            Please note that if you customize the wallet parameters or restart the device, the descriptor will be unloaded, and you may need to load it again to check addresses.

            "},{"location":"getting-started/usage/navigating-the-main-menu/#passphrase","title":"Passphrase","text":"

            If you forgot to load a passphrase while loading your wallet, or if you use multiple passphrases with the same mnemonic, you can add, replace, or remove a passphrase here. Simply choose between typing or scanning it.

            To remove a passphrase, select \"Type BIP39 Passphrase,\" leave the field blank, and press \"Go.\"

            Don't forget to verify the resulting fingerprint in the status bar to ensure you've loaded the correct key.

            "},{"location":"getting-started/usage/navigating-the-main-menu/#customize","title":"Customize","text":"

            Here you are presented to the exact same customization options you have while loading a key and wallet. You can change the Network, Single/Multisig, Script Type and Account. More about wallet attributes

            "},{"location":"getting-started/usage/navigating-the-main-menu/#bip85","title":"BIP85","text":"

            Bitcoin BIP85, also known as the Deterministic Entropy From BIP32 Keychains, allows for the generation of deterministic entropy using a BIP32 master key. This entropy can then be used to create various cryptographic keys and mnemonics (e.g., BIP39 seed phrases). BIP85 ensures that all derived keys and mnemonics are deterministic and reproducible, meaning they can be recreated from the same master key. This feature is useful for securely managing multiple child keys from a single master key without the need to store each one separately.

            Choose between a 12 or 24 words child then type the desired index to export a child mnemonic. After being presented to the new mnemonic, you can choose to load and use it right away.

            Please note passphrases will be removed when loading a BIP85 child.

            "},{"location":"getting-started/usage/navigating-the-main-menu/#address","title":"Address","text":"

            Scan, verify, export or print your wallet addresses.

            "},{"location":"getting-started/usage/navigating-the-main-menu/#scan-address","title":"Scan Address","text":"

            This option turns on the camera and allows you to scan in a QR code of a receive address. Upon scanning, it will render its own QR code of the address back to the display along with the (text) address below it. You could use this feature to scan the address of someone you want to send coins to and display the QR back to your wallet coordinator rather than copy-pasting an address. If you have a thermal printer attached, you can also print this QR code.

            After proceeding through this screen, you will be asked if you want to check that the address belongs to your wallet. If you confirm, it will exhaustively search through as many addresses derived from your wallet as you want in order to find a match.

            This option exists as an extra security check to verify that the address your wallet coordinator has generated is authentic and belongs to your wallet.

            "},{"location":"getting-started/usage/navigating-the-main-menu/#receive-addresses","title":"Receive Addresses","text":"

            List your wallet receiving addresses, you can browse to select an arbitrary address to show your QR code and print if you want.

            "},{"location":"getting-started/usage/navigating-the-main-menu/#change-addresses","title":"Change Addresses","text":"

            List your wallet change addresses, you can browse to select an arbitrary address to show your QR code and print if you want.

            "},{"location":"getting-started/usage/navigating-the-main-menu/#sign","title":"Sign","text":"

            Under Sign, you can choose to sign a PSBT or a message. You can load both PSBTs and messages scanning QR codes or loading from files on a SD card.

            "},{"location":"getting-started/usage/navigating-the-main-menu/#psbt","title":"PSBT","text":"

            To sign a Bitcoin PSBT, you have the following options:

            • Scan an Animated QR Code: Turn on the camera and scan an animated QR code of a PSBT generated by your wallet coordinator software. If you have any issues, see Troubleshooting.
            • Load from SD Card: Load an unsigned PSBT file from your SD card.

            Upon loading the PSBT, you will be presented with a preview showing the amount of BTC being sent, the recipient's address, and the transaction fee. Amounts are displayed according to your locale and the International Bureau of Weights and Measures, while still adhering to the concept of the Satcomma standard format.

            If you choose to proceed and sign the transaction, the signed PSBT can be exported in two ways:

            • As an animated QR code, which can be scanned back into your coordinator wallet.
            • As a signed PSBT file, which can be saved to your SD card and then loaded back into your coordinator wallet for broadcasting.

            If a thermal printer is attached to your device, you can also print the PSBT QR codes for record-keeping or further processing.

            "},{"location":"getting-started/usage/navigating-the-main-menu/#message","title":"Message","text":"

            Similar to PSBTs, Krux can load, sign, and export signatures for messages. This feature allows you to attest not only to the ownership of the messages themselves but also to the ownership of Bitcoin addresses and the authorship of documents and files.

            "},{"location":"getting-started/usage/navigating-the-main-menu/#standard-messages-and-files","title":"Standard Messages and Files","text":"

            You can scan or load a file from an SD card, the content can be plaintext or the SHA-256 hash of a message. Upon loading, you will be shown a preview of the message's SHA-256 hash for confirmation before signing.

            If you confirm, a signature will be generated, and you will see a base64-encoded version of it. You can then choose to export it as a QR code or save it to an SD card. If a thermal printer is attached, you can also print the QR code.

            Following this, you will see and be allowed to export your raw (master) public key in hexadecimal form, which can be used by others to verify your signature. If a thermal printer is attached, you can also print this QR code.

            This feature is used to sign Krux releases, airgapped, using a Krux device.

            "},{"location":"getting-started/usage/navigating-the-main-menu/#messages-at-address","title":"Messages at Address","text":"

            Coordinators like Sparrow and Specter offer the possibility to sign messages at a Bitcoin receive address, allowing you to attest ownership of that address. Krux will detect if the message is of this type and present a similar workflow for signing. The main difference is that the address will be displayed along with the raw message, and since the message is signed with a derived address instead of the master public key, Krux won't offer the option to export the raw public key after the signature.

            "},{"location":"getting-started/usage/setting-a-coordinator-and-signing/","title":"Setting a Coordinator and Signing","text":"

            After creating a mnemonic, making a safe backup, and testing to recover your mnemonic, it's time to set up a coordinator.

            Krux can work with multiple coordinator wallets. Popular options include:

            • Sparrow Wallet (desktop)

            • Specter Desktop (desktop)

            • Nunchuk (mobile)

            • BlueWallet (mobile)

            "},{"location":"getting-started/usage/setting-a-coordinator-and-signing/#step-1-install-the-coordinator-wallet","title":"Step 1: Install the Coordinator Wallet","text":"

            Download and install the appropriate version of your chosen coordinator wallet for your device and operating system.

            "},{"location":"getting-started/usage/setting-a-coordinator-and-signing/#step-2-create-a-new-wallet-with-krux-as-a-signer","title":"Step 2: Create a New Wallet with Krux as a Signer","text":"

            Depending on the coordinator, the steps to add Krux as a signer may vary slightly:

            Specter and Nunchuk Single-sig: Add Krux as signer device, then create a wallet that uses it.

            Specter and Nunchuk Multisig: Add Krux as signer device, add other devices, then create a wallet that uses them.

            Sparrow and BlueWallet: Create a wallet (or vault in Blue Wallet) first, then add signer device(s).

            1. Load a mnemonic and wallet in Krux.

            1. On your coordinator, when presented with possible signer devices to add, choose Krux if available, otherwise choose \"other\" or even another QR code compatible signer. As Krux is compatible with many QR code formats, most of available alternatives should work.

            2. When prompted by your coordinator to import signer's public key, access the \"Extended Public Key\" on Krux.

            1. Export an XPUB (or YPUB, ZPUB, etc., based on the script type) as a QR code.

            1. Scan this QR code with your coordinator.

            2. Ensure the coordinator\u2019s wallet attributes (policy type, script type, fingerprint, and derivation) match those in Krux.

            "},{"location":"getting-started/usage/setting-a-coordinator-and-signing/#step-3-load-and-backup-wallet-descriptor-multisig-only","title":"Step 3: Load and Backup Wallet Descriptor (Multisig Only)","text":"
            1. In your coordinator, export the wallet descriptor containing information about the wallet and all cosigners:
              • Sparrow: \"Descriptor\"
              • Specter: \"Export Wallet\"
              • Nunchuk: \"Export Wallet Configuration\"
              • BlueWallet: \"Export Coordination Setup\"
            2. Export the descriptor as a QR code or file.
            3. On Krux, go to \"Wallet\" -> \"Wallet Descriptor\" to scan the descriptor QR code or load it via SD card.
            1. If you access \"Wallet\" -> \"Wallet Descriptor\" again, you will be able to:

              • Check the wallet cosigners.
              • Save the descriptor on an SD card (useful if you initially loaded it from QR codes).

              It is crucial to have a backup of this descriptor to recover your wallet in case one of the cosigners is lost.

            "},{"location":"getting-started/usage/setting-a-coordinator-and-signing/#step-4-verify-addresses","title":"Step 4: Verify Addresses","text":"

            For single-sig or multisig (after loading a descriptor):

            • Go to \"Address\" on Krux.

            • List \"Receive Addresses\" and \"Change Addresses\" or use \"Scan Address\" to verify if addresses from your coordinator are matched by Krux.

            "},{"location":"getting-started/usage/setting-a-coordinator-and-signing/#step-5-funding-your-wallet","title":"Step 5: Funding your Wallet","text":"

            Once addresses are verified, send a small test amount to your wallet. Test signing and sending a transaction before adding more funds.

            "},{"location":"getting-started/usage/setting-a-coordinator-and-signing/#step-6-sign-psbts-and-messages","title":"Step 6: Sign PSBTs and Messages","text":""},{"location":"getting-started/usage/setting-a-coordinator-and-signing/#psbts","title":"PSBTs","text":"
            1. Create a transaction in your coordinator.

            2. Export the transaction as a QR code.

            3. On Krux, go to \"Sign\" -> \"PSBT\" -> \"Load from camera\".

            4. Scan the animated QR code.

            5. Verify the transaction details.

            6. If correct, press \"Sign to QR code\".

            7. Scan the signed transaction QR code back into the coordinator to broadcast it.

            Alternatively, you can use an SD card:

            Save the transaction as a file on an SD card. On Krux, go to \"Sign\" -> \"PSBT\" -> \"Load from SD card\" and \"Sign to SD card\". Load the signed transaction on the coordinator and broadcast it.

            "},{"location":"getting-started/usage/setting-a-coordinator-and-signing/#messages","title":"Messages","text":"

            Some coordinators, like Sparrow, allow you to sign messages linked to your wallet's addresses. Signing and verifying a message signature attests to the ownership of an address and serves as an additional test for your setup.

            "},{"location":"getting-started/usage/video-tutorials/","title":"Video Tutorials","text":""},{"location":"getting-started/usage/video-tutorials/#krux-video-tutorials","title":"Krux Video Tutorials","text":"

            Most people prefer to learn by watching videos, and we are fortunate to have excellent content creators in the Bitcoin space, here are some examples of Krux related content and tutorials.

            "},{"location":"getting-started/usage/video-tutorials/#english","title":"English","text":""},{"location":"getting-started/usage/video-tutorials/#krux-on-m5stickv-sparrow","title":"Krux on M5StickV + Sparrow","text":"

            Krux on M5StickV + Sparrow Wallet by Crypto Guide

            "},{"location":"getting-started/usage/video-tutorials/#krux-on-maix-amigo-blue","title":"Krux on Maix Amigo + Blue","text":"

            Krux on Maix Amigo + Blue Wallet by Crypto Guide

            "},{"location":"getting-started/usage/video-tutorials/#diy-only-multisig","title":"DIY-Only MultiSig","text":"

            DIY-Only Multivendor Hardware Wallet MultiSig: SeedSigner, Jade, Krux, Satochip + Sparrow & Electrum by Crypto Guide

            "},{"location":"getting-started/usage/video-tutorials/#build-from-source-verify","title":"Build From Source & Verify","text":"

            Krux DIY Bitcoin Signer: Build From Source & Verify (With Windows + WSL2 + Docker) by Crypto Guide

            "},{"location":"getting-started/usage/video-tutorials/#portuguese","title":"Portuguese","text":""},{"location":"getting-started/usage/video-tutorials/#krux-facil-de-instalar","title":"Krux f\u00e1cil de instalar","text":"

            Hardwallet Krux f\u00e1cil de instalar + QRs criptografados - por Bitdov

            "},{"location":"getting-started/usage/video-tutorials/#multisig-com-krux","title":"Multisig com Krux","text":"

            Multisig com Krux e Nunchuk no celular - por Bitdov

            "},{"location":"getting-started/usage/video-tutorials/#krux-com-impressora-termica","title":"Krux com impressora t\u00e9rmica","text":"

            Usando a Krux com impressora t\u00e9rmica - por Bitdov

            "},{"location":"getting-started/usage/video-tutorials/#krux-no-celular","title":"Krux no celular","text":"

            Carteira Bitcoin com celular OFFLINE - Krux mobile APK - por Dig

            "},{"location":"getting-started/usage/video-tutorials/#krux-no-celular-ii","title":"Krux no celular II","text":"

            Como utilizar a carteira Krux no celular - por Jo\u00e3o Trein

            "},{"location":"getting-started/usage/video-tutorials/#faca-sua-krux","title":"Fa\u00e7a sua Krux","text":"

            Fa\u00e7a sua hardware wallet em casa com a KRUX! - por Caiovski

            "}]} \ No newline at end of file diff --git a/sitemap.xml b/sitemap.xml index 0145996a7..0a6bbfb88 100644 --- a/sitemap.xml +++ b/sitemap.xml @@ -1,234 +1,153 @@ - https://selfcustody.github.io/krux/faq/ - 2024-04-19 - daily - - + https://selfcustody.github.io/krux/faq/ + 2024-07-15 + daily + - https://selfcustody.github.io/krux/ - 2024-04-19 - daily - - + https://selfcustody.github.io/krux/ + 2024-07-15 + daily + - https://selfcustody.github.io/krux/parts/ - 2024-04-19 - daily - - + https://selfcustody.github.io/krux/parts/ + 2024-07-15 + daily + - https://selfcustody.github.io/krux/support/ - 2024-04-19 - daily - - + https://selfcustody.github.io/krux/support/ + 2024-07-15 + daily + - https://selfcustody.github.io/krux/getting-started/ - 2024-04-19 - daily - - + https://selfcustody.github.io/krux/troubleshooting/ + 2024-07-15 + daily + - https://selfcustody.github.io/krux/getting-started/navigation/ - 2024-04-19 - daily - - + https://selfcustody.github.io/krux/uncommon-questions/ + 2024-07-15 + daily + - https://selfcustody.github.io/krux/getting-started/settings/ - 2024-04-19 - daily - - + https://selfcustody.github.io/krux/getting-started/ + 2024-07-15 + daily + - https://selfcustody.github.io/krux/getting-started/features/QR-transcript-tools/ - 2024-04-19 - daily - - + https://selfcustody.github.io/krux/getting-started/navigation/ + 2024-07-15 + daily + - https://selfcustody.github.io/krux/getting-started/features/encrypted-mnemonics/ - 2024-04-19 - daily - - + https://selfcustody.github.io/krux/getting-started/settings/ + 2024-07-15 + daily + - https://selfcustody.github.io/krux/getting-started/features/printing/ - 2024-04-19 - daily - - + https://selfcustody.github.io/krux/getting-started/features/QR-transcript-tools/ + 2024-07-15 + daily + - https://selfcustody.github.io/krux/getting-started/features/sd-card-update/ - 2024-04-19 - daily - - + https://selfcustody.github.io/krux/getting-started/features/encrypted-mnemonics/ + 2024-07-15 + daily + - https://selfcustody.github.io/krux/getting-started/features/tools/ - 2024-04-19 - daily - - + https://selfcustody.github.io/krux/getting-started/features/entropy/ + 2024-07-15 + daily + - https://selfcustody.github.io/krux/getting-started/installing/from-gui/ - 2024-04-19 - daily - - + https://selfcustody.github.io/krux/getting-started/features/printing/ + 2024-07-15 + daily + - https://selfcustody.github.io/krux/getting-started/installing/from-pre-built-release/ - 2024-04-19 - daily - - + https://selfcustody.github.io/krux/getting-started/features/sd-card-update/ + 2024-07-15 + daily + - https://selfcustody.github.io/krux/getting-started/installing/from-source/ - 2024-04-19 - daily - - + https://selfcustody.github.io/krux/getting-started/features/tools/ + 2024-07-15 + daily + - https://selfcustody.github.io/krux/getting-started/installing/from-test-release/ - 2024-04-19 - daily - - + https://selfcustody.github.io/krux/getting-started/installing/from-gui/ + 2024-07-15 + daily + - https://selfcustody.github.io/krux/getting-started/installing/ - 2024-04-19 - daily - - + https://selfcustody.github.io/krux/getting-started/installing/from-pre-built-release/ + 2024-07-15 + daily + - https://selfcustody.github.io/krux/getting-started/usage/generating-a-mnemonic/ - 2024-04-19 - daily - - + https://selfcustody.github.io/krux/getting-started/installing/from-source/ + 2024-07-15 + daily + - https://selfcustody.github.io/krux/getting-started/usage/loading-a-mnemonic/ - 2024-04-19 - daily - - + https://selfcustody.github.io/krux/getting-started/installing/from-test-release/ + 2024-07-15 + daily + - https://selfcustody.github.io/krux/getting-started/usage/navigating-the-main-menu/ - 2024-04-19 - daily - - + https://selfcustody.github.io/krux/getting-started/installing/ + 2024-07-15 + daily + - https://selfcustody.github.io/krux/getting-started/usage/using-a-multisig-wallet/ - 2024-04-19 - daily - - + https://selfcustody.github.io/krux/getting-started/usage/generating-a-mnemonic/ + 2024-07-15 + daily + - https://selfcustody.github.io/krux/getting-started/usage/using-a-single-sig-wallet/ - 2024-04-19 - daily - - + https://selfcustody.github.io/krux/getting-started/usage/loading-a-mnemonic/ + 2024-07-15 + daily + - https://selfcustody.github.io/krux/snippets/12th-24th-word-generate/ - 2024-04-19 - daily - - + https://selfcustody.github.io/krux/getting-started/usage/navigating-the-main-menu/ + 2024-07-15 + daily + - https://selfcustody.github.io/krux/snippets/amigo-more-info-faq/ - 2024-04-19 - daily - - + https://selfcustody.github.io/krux/getting-started/usage/setting-a-coordinator-and-signing/ + 2024-07-15 + daily + - https://selfcustody.github.io/krux/snippets/flash-krux-logo/ - 2024-04-19 - daily - - - - - https://selfcustody.github.io/krux/snippets/m5stickv-usb-c/ - 2024-04-19 - daily - - - - - https://selfcustody.github.io/krux/snippets/sd-card-info-faq/ - 2024-04-19 - daily - - - - - https://selfcustody.github.io/krux/snippets/single-multi-wallet-descriptor/ - 2024-04-19 - daily - - - - - https://selfcustody.github.io/krux/snippets/tips-after-install/ - 2024-04-19 - daily - - - - - https://selfcustody.github.io/krux/snippets/sparrow/broadcast-sparrow/ - 2024-04-19 - daily - - - - - https://selfcustody.github.io/krux/snippets/sparrow/import-xpub-sparrow/ - 2024-04-19 - daily - - - - - https://selfcustody.github.io/krux/snippets/sparrow/load-wallet-output-sparrow/ - 2024-04-19 - daily - - - - - https://selfcustody.github.io/krux/snippets/sparrow/send-coins-sparrow/ - 2024-04-19 - daily - - + https://selfcustody.github.io/krux/getting-started/usage/video-tutorials/ + 2024-07-15 + daily + \ No newline at end of file diff --git a/sitemap.xml.gz b/sitemap.xml.gz index c97a61a05..ab926173e 100644 Binary files a/sitemap.xml.gz and b/sitemap.xml.gz differ diff --git a/snippets/12th-24th-word-generate.txt b/snippets/12th-24th-word-generate.txt new file mode 100644 index 000000000..2f9e08107 --- /dev/null +++ b/snippets/12th-24th-word-generate.txt @@ -0,0 +1 @@ +On your 12th or 24th word, you can leave it blank to have Krux generate the final checksum word of your mnemonic for you. This is useful when you choose a manual method to generate your mnemonic and want the final BIP-39 checksum word to be valid. \ No newline at end of file diff --git a/snippets/12th-24th-word-generate/index.html b/snippets/12th-24th-word-generate/index.html deleted file mode 100644 index 00fa8921a..000000000 --- a/snippets/12th-24th-word-generate/index.html +++ /dev/null @@ -1,1126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - 12th 24th word generate - Krux - Open-source signing device firmware for Bitcoin - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
            - -
            -
            - -
            - - - - - - -
            - - - - - - - -
            - -
            - - - - -
            -
            - - - -
            -
            -
            - - - - - - - - - -
            -
            -
            - - - - -
            -
            - - - - - - - -

            12th 24th word generate

            - -

            On your 12th or 24th word, you can leave it blank to have Krux generate the final checksum word of your mnemonic for you. This is useful when you choose a manual method to generate your mnemonic and want the final BIP-39 checksum word to be valid.

            - - - - - - - - - - - - - -
            -
            - - - -
            - - - -
            - - - -
            -
            -
            -
            - - - - - - - - - - \ No newline at end of file diff --git a/snippets/amigo-more-info-faq.txt b/snippets/amigo-more-info-faq.txt new file mode 100644 index 000000000..91f8908a0 --- /dev/null +++ b/snippets/amigo-more-info-faq.txt @@ -0,0 +1,2 @@ +##### A note about the Maix Amigo +Some Amigo screens have inverted X coordinates, others display colors differently. For more info see [Troubleshooting](/krux/troubleshooting/#troubleshooting-lcd-settings-on-maix-amigo). diff --git a/snippets/amigo-more-info-faq/index.html b/snippets/amigo-more-info-faq/index.html deleted file mode 100644 index 0f87f974a..000000000 --- a/snippets/amigo-more-info-faq/index.html +++ /dev/null @@ -1,1132 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - Amigo more info faq - Krux - Open-source signing device firmware for Bitcoin - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
            - -
            - - - - - - -
            - - - - - - - -
            - -
            - - - - -
            -
            - - - -
            -
            -
            - - - - - - - - - -
            -
            -
            - - - - -
            -
            - - - - - - - -

            Amigo more info faq

            - -
            A note about the Maix Amigo
            -

            Some Amigo screens have inverted X coordinates, others display colors differently. For more info see FAQ

            - - - - - - - - - - - - - -
            -
            - - - -
            - - - -
            - - - -
            -
            -
            -
            - - - - - - - - - - \ No newline at end of file diff --git a/snippets/camera-scan-tips.txt b/snippets/camera-scan-tips.txt new file mode 100644 index 000000000..0fa7b4ee3 --- /dev/null +++ b/snippets/camera-scan-tips.txt @@ -0,0 +1 @@ +If you are in a dark environment, you can hold down the `ENTER` button of the M5StickV or Maix Amigo to turn on their LED light to potentially increase visibility. Some cameras (`OV7740` and `OV2640`) have an anti-glare mode to better capture images from high brightness screens or with incident light, they are present on M5StickV, Amigo, Cube and some Yahboom. To enable/disable the anti-glare mode on a supported device just press the `PAGE` button while scanning. \ No newline at end of file diff --git a/snippets/flash-krux-logo.txt b/snippets/flash-krux-logo.txt new file mode 100644 index 000000000..bfeca470f --- /dev/null +++ b/snippets/flash-krux-logo.txt @@ -0,0 +1,8 @@ +When the flashing process completes, you should see the Krux logo: + + + + +If it doesn't, try turning your device off and on by holding down the power button for six seconds. + +Congrats, you're now running Krux! \ No newline at end of file diff --git a/snippets/flash-krux-logo/index.html b/snippets/flash-krux-logo/index.html deleted file mode 100644 index dd40349ec..000000000 --- a/snippets/flash-krux-logo/index.html +++ /dev/null @@ -1,1130 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - Flash krux logo - Krux - Open-source signing device firmware for Bitcoin - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
            - -
            -
            - -
            - - - - - - -
            - - - - - - - -
            - -
            - - - - -
            -
            - - - -
            -
            -
            - - - - - - - - - -
            -
            -
            - - - - -
            -
            - - - - - - - -

            Flash krux logo

            - -

            When the flashing process completes, you should see the Krux logo:

            -

            -

            -

            If it doesn't, try turning your device off and on by holding down the power button for six seconds.

            -

            Congrats, you're now running Krux!

            - - - - - - - - - - - - - -
            -
            - - - -
            - - - -
            - - - -
            -
            -
            -
            - - - - - - - - - - \ No newline at end of file diff --git a/snippets/m5stickv-usb-c.txt b/snippets/m5stickv-usb-c.txt new file mode 100644 index 000000000..e95310b32 --- /dev/null +++ b/snippets/m5stickv-usb-c.txt @@ -0,0 +1 @@ +M5StickV's USB-C port lacks pull up resistors required for it to be recognized and powered by host (computer) USB-C ports. If you don't have an USB-A available, you can use a USB hub connected between your computer's USB-C and M5StickV. \ No newline at end of file diff --git a/snippets/m5stickv-usb-c/index.html b/snippets/m5stickv-usb-c/index.html deleted file mode 100644 index e734542d9..000000000 --- a/snippets/m5stickv-usb-c/index.html +++ /dev/null @@ -1,1126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - M5stickv usb c - Krux - Open-source signing device firmware for Bitcoin - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
            - -
            -
            - -
            - - - - - - -
            - - - - - - - -
            - -
            - - - - -
            -
            - - - -
            -
            -
            - - - - - - - - - -
            -
            -
            - - - - -
            -
            - - - - - - - -

            M5stickv usb c

            - -

            M5stickV's USB-C port lacks pull up resistors required for it to be recognized and powered by host (computer) USB-C ports. If you don't have an USB-A available, you can use a USB hub connected between your computer's USB-C and M5stickV.

            - - - - - - - - - - - - - -
            -
            - - - -
            - - - -
            - - - -
            -
            -
            -
            - - - - - - - - - - \ No newline at end of file diff --git a/snippets/sd-card-info-faq.txt b/snippets/sd-card-info-faq.txt new file mode 100644 index 000000000..5969206bd --- /dev/null +++ b/snippets/sd-card-info-faq.txt @@ -0,0 +1 @@ +We cannot guarantee that a microSD card is compatible and will work in your device; you'll need to test it on the device to be sure, read the [FAQ](/krux/faq/#why-isnt-krux-detecting-my-microsd-card-or-presenting-an-error) for more info. \ No newline at end of file diff --git a/snippets/sd-card-info-faq/index.html b/snippets/sd-card-info-faq/index.html deleted file mode 100644 index ca8f91449..000000000 --- a/snippets/sd-card-info-faq/index.html +++ /dev/null @@ -1,1126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - Sd card info faq - Krux - Open-source signing device firmware for Bitcoin - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
            - -
            -
            - -
            - - - - - - -
            - - - - - - - -
            - -
            - - - - -
            -
            - - - -
            -
            -
            - - - - - - - - - -
            -
            -
            - - - - -
            -
            - - - - - - - -

            Sd card info faq

            - -

            We cannot guarantee that a microSD card is compatible and will work in your device; you'll need to test it on the device to be sure, read the FAQ for more info.

            - - - - - - - - - - - - - -
            -
            - - - -
            - - - -
            - - - -
            -
            -
            -
            - - - - - - - - - - \ No newline at end of file diff --git a/snippets/single-multi-wallet-descriptor/index.html b/snippets/single-multi-wallet-descriptor/index.html deleted file mode 100644 index d644c0c2e..000000000 --- a/snippets/single-multi-wallet-descriptor/index.html +++ /dev/null @@ -1,1129 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - Single multi wallet descriptor - Krux - Open-source signing device firmware for Bitcoin - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
            - -
            -
            - -
            - - - - - - -
            - - - - - - - -
            - -
            - - - - -
            -
            - - - -
            -
            -
            - - - - - - - - - -
            -
            -
            - - - - -
            -
            - - - - - - - -

            Single multi wallet descriptor

            - -

            In Krux, select the Wallet Descriptor menu item option and scan the QR code.

            -

            -

            -

            If it worked, Krux should display the wallet information that it loaded:

            - - - - - - - - - - - - - -
            -
            - - - -
            - - - -
            - - - -
            -
            -
            -
            - - - - - - - - - - \ No newline at end of file diff --git a/snippets/sparrow/broadcast-sparrow.txt b/snippets/sparrow/broadcast-sparrow.txt new file mode 100644 index 000000000..ea496cc9c --- /dev/null +++ b/snippets/sparrow/broadcast-sparrow.txt @@ -0,0 +1,5 @@ +Click the blue *Broadcast Transaction* button and your transaction should be broadcasted to the network! + + + +Congratulations, you have learned how to send a transaction using Sparrow 🎉 \ No newline at end of file diff --git a/snippets/sparrow/broadcast-sparrow/index.html b/snippets/sparrow/broadcast-sparrow/index.html deleted file mode 100644 index 7e7262d16..000000000 --- a/snippets/sparrow/broadcast-sparrow/index.html +++ /dev/null @@ -1,1128 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - Broadcast sparrow - Krux - Open-source signing device firmware for Bitcoin - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
            - -
            -
            - -
            - - - - - - -
            - - - - - - - -
            - -
            - - - - -
            -
            - - - -
            -
            -
            - - - - - - - - - -
            -
            -
            - - - - -
            -
            - - - - - - - -

            Broadcast sparrow

            - -

            Click the blue Broadcast Transaction button and your transaction should be broadcasted to the network!

            -

            -

            Congratulations, you have learned how to send a transaction using Sparrow 🎉

            - - - - - - - - - - - - - -
            -
            - - - -
            - - - -
            - - - -
            -
            -
            -
            - - - - - - - - - - \ No newline at end of file diff --git a/snippets/sparrow/import-xpub-sparrow.txt b/snippets/sparrow/import-xpub-sparrow.txt new file mode 100644 index 000000000..7aa385d10 --- /dev/null +++ b/snippets/sparrow/import-xpub-sparrow.txt @@ -0,0 +1,12 @@ +Now, you will need to import your public key. To do so, press the *Airgapped Hardware Wallet* button under *Keystores* section. On the screen that pops up, look for *Krux* option and click its :camera: *Scan...* button. + + + + + +On your Krux device, navigate to the *Extended Public Key* option in the main menu and click on **XPUB - QR Code** and show it to Sparrow. + + + + +It should import the xpub and show a key tab under *Keystores* section: \ No newline at end of file diff --git a/snippets/sparrow/import-xpub-sparrow/index.html b/snippets/sparrow/import-xpub-sparrow/index.html deleted file mode 100644 index 5bdfa3ef5..000000000 --- a/snippets/sparrow/import-xpub-sparrow/index.html +++ /dev/null @@ -1,1132 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - Import xpub sparrow - Krux - Open-source signing device firmware for Bitcoin - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
            - -
            -
            - -
            - - - - - - -
            - - - - - - - -
            - -
            - - - - -
            -
            - - - -
            -
            -
            - - - - - - - - - -
            -
            -
            - - - - -
            -
            - - - - - - - -

            Import xpub sparrow

            - -

            Now, you will need to import your public key. To do so, press the Airgapped Hardware Wallet button under Keystores section. On the screen that pops up, look for Krux option and click its 📷 Scan... button.

            -

            -

            -

            On your Krux device, navigate to the Extended Public Key option in the main menu and click on XPUB - QR Code and show it to Sparrow.

            -

            -

            -

            It should import the xpub and show a key tab under Keystores section:

            - - - - - - - - - - - - - -
            -
            - - - -
            - - - -
            - - - -
            -
            -
            -
            - - - - - - - - - - \ No newline at end of file diff --git a/snippets/sparrow/load-wallet-output-sparrow.txt b/snippets/sparrow/load-wallet-output-sparrow.txt new file mode 100644 index 000000000..7a75a7b07 --- /dev/null +++ b/snippets/sparrow/load-wallet-output-sparrow.txt @@ -0,0 +1,6 @@ +In Krux, navigate to "Wallet" -> "Wallet Descriptor" and choose to load a descriptor from QR code or SD card. + + + + +After the descriptor is loaded, Krux will present basic information about it: \ No newline at end of file diff --git a/snippets/sparrow/load-wallet-output-sparrow/index.html b/snippets/sparrow/load-wallet-output-sparrow/index.html deleted file mode 100644 index 61cfa0cc6..000000000 --- a/snippets/sparrow/load-wallet-output-sparrow/index.html +++ /dev/null @@ -1,1129 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - Load wallet output sparrow - Krux - Open-source signing device firmware for Bitcoin - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
            - -
            -
            - -
            - - - - - - -
            - - - - - - - -
            - -
            - - - - -
            -
            - - - -
            -
            -
            - - - - - - - - - -
            -
            -
            - - - - -
            -
            - - - - - - - -

            Load wallet output sparrow

            - -

            In Krux, select the Wallet menu item option and scan the QR code.

            -

            -

            -

            If it worked, Krux should display the wallet information that it loaded:

            - - - - - - - - - - - - - -
            -
            - - - -
            - - - -
            - - - -
            -
            -
            -
            - - - - - - - - - - \ No newline at end of file diff --git a/snippets/sparrow/send-coins-sparrow.txt b/snippets/sparrow/send-coins-sparrow.txt new file mode 100644 index 000000000..c8ef12212 --- /dev/null +++ b/snippets/sparrow/send-coins-sparrow.txt @@ -0,0 +1,24 @@ +### Send coins +Go to the *Send* screen, fill in the recipient address, amount, and any extra information you wish to supply, and click the blue *Create Transaction* button. + + + +On the next screen, make sure that the *Signing Wallet* is the one you created and that the *Sighash* is set to *All*. Click the blue *Finalize Transaction for Signing* button. + + + +On the next screen, click *Show QR* to make Sparrow display an animated QR code of the PSBT that you can scan with Krux by going to *Sign > PSBT > Load from camera* in its main menu. + + + +After scanning, Krux should display info about the transaction for you to confirm before signing. + + + + +Once you have confirmed, Krux will begin animating a QR code of the signed transaction that you can scan into Sparrow. + + + + +In Sparrow, click *Scan QR* and show it the QR. A progress bar will indicate how many parts of the QR have been read. \ No newline at end of file diff --git a/snippets/sparrow/send-coins-sparrow/index.html b/snippets/sparrow/send-coins-sparrow/index.html deleted file mode 100644 index ffdb094f2..000000000 --- a/snippets/sparrow/send-coins-sparrow/index.html +++ /dev/null @@ -1,1144 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - Send coins sparrow - Krux - Open-source signing device firmware for Bitcoin - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
            - -
            - - - - - - -
            - - - - - - - -
            - -
            - - - - -
            -
            - - - -
            -
            -
            - - - - - - - - - -
            -
            -
            - - - - -
            -
            - - - - - - - -

            Send coins sparrow

            - -

            Send coins

            -

            Go to the Send screen, fill in the recipient address, amount, and any extra information you wish to supply, and click the blue Create Transaction button.

            -

            -

            On the next screen, make sure that the Signing Wallet is the one you created and that the Sighash is set to All. Click the blue Finalize Transaction for Signing button.

            -

            -

            On the next screen, click Show QR to make Sparrow display an animated QR code of the PSBT that you can scan with Krux by going to Sign > PSBT > Load from camera in its main menu.

            -

            -

            After scanning, Krux should display info about the transaction for you to confirm before signing.

            -

            -

            -

            Once you have confirmed, Krux will begin animating a QR code of the signed transaction that you can scan into Sparrow.

            -

            -

            -

            In Sparrow, click Scan QR and show it the QR. A progress bar will indicate how many parts of the QR have been read.

            - - - - - - - - - - - - - -
            -
            - - - -
            - - - -
            - - - -
            -
            -
            -
            - - - - - - - - - - \ No newline at end of file diff --git a/snippets/tips-after-install.txt b/snippets/tips-after-install.txt new file mode 100644 index 000000000..8518deee0 --- /dev/null +++ b/snippets/tips-after-install.txt @@ -0,0 +1,5 @@ +### Multilingual support +Prefer a different language? Krux has support for multiple languages. Once at the start screen, go to `Settings`, followed by `Locale`, and select the locale you wish to use. + +### Upgrade via microSD card +Once you've installed the initial firmware on your device via USB, you can either continue updating the device by flashing or you can perform upgrades [via microSD](../features/sd-card-update.md) card to keep the device airgapped. \ No newline at end of file diff --git a/snippets/tips-after-install/index.html b/snippets/tips-after-install/index.html deleted file mode 100644 index 54cb4ee6d..000000000 --- a/snippets/tips-after-install/index.html +++ /dev/null @@ -1,1134 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - Tips after install - Krux - Open-source signing device firmware for Bitcoin - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
            - -
            - - - - - - -
            - - - - - - - -
            - -
            - - - - -
            -
            - - - -
            -
            -
            - - - - - - - - - -
            -
            -
            - - - - -
            -
            - - - - - - - -

            Tips after install

            - -

            Multilingual support

            -

            Prefer a different language? Krux has support for multiple languages. Once at the start screen, go to Settings, followed by Locale, and select the locale you wish to use.

            -

            Upgrade via microSD card

            -

            Once you've installed the initial firmware on your device via USB, you can either continue updating the device by flashing or you can perform upgrades via microSD card to keep the device airgapped.

            - - - - - - - - - - - - - -
            -
            - - - -
            - - - -
            - - - -
            -
            -
            -
            - - - - - - - - - - \ No newline at end of file diff --git a/snippets/warning-printer.txt b/snippets/warning-printer.txt new file mode 100644 index 000000000..f27f050e7 --- /dev/null +++ b/snippets/warning-printer.txt @@ -0,0 +1 @@ +**Warning/Disclaimer**: This feature is intended for individuals with experience in electronics tinkering and soldering. \ No newline at end of file diff --git a/support/index.html b/support/index.html index 51d0acd25..c0cb2fd90 100644 --- a/support/index.html +++ b/support/index.html @@ -13,12 +13,12 @@ - + - + @@ -26,7 +26,7 @@ - + @@ -50,6 +50,8 @@ + + @@ -146,7 +148,7 @@
          @@ -963,7 +1022,7 @@ - Devices and Part List + Devices and Parts List @@ -996,6 +1055,46 @@ + + +
        • + + + + + Troubleshooting + + + + +
        • + + + + + + + + + +
        • + + + + + Uncommon Questions + + + + +
        • + + + + + + + @@ -1132,16 +1231,15 @@

          Ways you can helpDevelopment

          Audit the code, file an issue, make a pull request, or do all three. :)

          Documentation

          -

          I'd like to see Krux help as many people as possible, and to do that, good documentation is needed. If you identify a better way to say something, please make a PR, any help is appreciated.

          +

          "I'd like to see Krux help as many people as possible, and to do that, good documentation is needed. If you identify a better way to say something, please make a PR, any help is appreciated." - Jeff

          Translation

          -

          Krux supports different languages. If you missed a language or saw a awkward translation, open an issue or make a PR! You can also make a difference by translating this documentation!

          +

          Krux supports different languages. If you missed a language or saw an awkward translation, open an issue or make a PR! You can also make a difference by translating this documentation! For information on how to translate, see here

          Social

          -

          Reach out our Telegram group or X profile for faster help, share ideas and join the Krux community. +

          Reach out via our Telegram group or X profile (Twitter) for faster help, share ideas and join the Krux community. Help others get to know Krux.

          "I'm an engineer, not a marketer. If you like Krux, help spread the word!" - Jeff

          Donations

          Krux won't ask for, receive, manage or distribute donations. You can donate directly to a Krux contributor of your choice.

          -

          Thank you!

          @@ -1184,7 +1282,7 @@

          Donations - +

    @@ -1226,7 +1324,7 @@

    Donations - + @@ -1234,15 +1332,15 @@

    Donations - + - - + + @@ -1250,7 +1348,7 @@

    Donations - +

    @@ -1268,7 +1366,13 @@

    Donations{"base": "..", "features": ["navigation.tabs", "navigation.tabs.sticky", "navigation.sections", "navigation.top", "navigation.indexes", "navigation.expand", "navigation.footer", "toc.integrate"], "search": "../assets/javascripts/workers/search.b8dbb3d2.min.js", "translations": {"clipboard.copied": "Copied to clipboard", "clipboard.copy": "Copy to clipboard", "search.result.more.one": "1 more on this page", "search.result.more.other": "# more on this page", "search.result.none": "No matching documents", "search.result.one": "1 matching document", "search.result.other": "# matching documents", "search.result.placeholder": "Type to start searching", "search.result.term.missing": "Missing", "select.version": "Select version"}} - + + + + + + + diff --git a/en/faq/index.html b/troubleshooting/index.html similarity index 63% rename from en/faq/index.html rename to troubleshooting/index.html index 2e1441543..6b6886614 100644 --- a/en/faq/index.html +++ b/troubleshooting/index.html @@ -10,28 +10,28 @@ - + - + - + - - + + - FAQ - Krux - Open-source signing device firmware for Bitcoin + Troubleshooting - Krux - Open-source signing device firmware for Bitcoin - + - + @@ -50,9 +50,11 @@ - + - + + + @@ -76,7 +78,7 @@
    - + Skip to content @@ -92,9 +94,9 @@
    - - - + + Support the Project + - - + + @@ -1126,22 +1183,12 @@ -

    Support the Project

    +

    Uncommon Questions

    -

    Ways you can help

    -

    Development

    -

    Audit the code, file an issue, make a pull request, or do all three. :)

    -

    Documentation

    -

    I'd like to see Krux help as many people as possible, and to do that, good documentation is needed. If you identify a better way to say something, please make a PR, any help is appreciated.

    -

    Translation

    -

    Krux supports different languages. If you missed a language or saw a awkward translation, open an issue or make a PR! You can also make a difference by translating this documentation!

    -

    Social

    -

    Reach out our Telegram group or X profile for faster help, share ideas and join the Krux community. -Help others get to know Krux.

    -

    "I'm an engineer, not a marketer. If you like Krux, help spread the word!" - Jeff

    -

    Donations

    -

    Krux won't ask for, receive, manage or distribute donations. You can donate directly to a Krux contributor of your choice.

    -

    Thank you!

    +

    What are all the features available?

    +

    On the official releases page you will find all the features listed, with details on the Getting Started page with a brief summary on the Navigation Overview page.

    +

    What is the purpose of using an SD card with the device?

    +

    SD card use is optional, but can be used to upgrade the firmware, save settings, cnc/file, QR codes, XPUBs, encrypted mnemonics, and also to save and load PSBTs, messages and wallet output descriptors.

    @@ -1149,14 +1196,9 @@

    DonationsDonations - +

    + + + + + + @@ -1226,7 +1282,7 @@

    Donations - + @@ -1234,15 +1290,15 @@

    Donations - + - - + + @@ -1250,7 +1306,7 @@

    Donations - +

    @@ -1265,10 +1321,16 @@

    Donations{"base": "../..", "features": ["navigation.tabs", "navigation.tabs.sticky", "navigation.sections", "navigation.top", "navigation.indexes", "navigation.expand", "navigation.footer", "toc.integrate"], "search": "../../assets/javascripts/workers/search.b8dbb3d2.min.js", "translations": {"clipboard.copied": "Copied to clipboard", "clipboard.copy": "Copy to clipboard", "search.result.more.one": "1 more on this page", "search.result.more.other": "# more on this page", "search.result.none": "No matching documents", "search.result.one": "1 matching document", "search.result.other": "# matching documents", "search.result.placeholder": "Type to start searching", "search.result.term.missing": "Missing", "select.version": "Select version"}} + - + + + + + + +