{
  "schemaVersion": 1,
  "slug": "password-generator",
  "category": "productivity",
  "kind": "implementation",
  "independentValidation": false,
  "implementationHash": "9a0bdb2480f81bf67735abc4ac6500644eb2f7fc79b075949919d5aff2d3a026",
  "provenance": "Generated from the calculation contract used by the published page. Publication is not external validation.",
  "sourceFiles": [
    "data/calculators/password-generator.json",
    "src/engine.js"
  ],
  "runtimeSource": "/sources/engine.txt",
  "runtimeHash": "5c888d8898afbdee36267bb9977829e1e2ac568f6677c8ee87469c139864645c",
  "scope": "The compute function records implemented equations, constants, lookup tables and custom rules. Inputs record defaults and allowed values; outputs record precision and units. Defaults are examples, not recommendations. General background references are not evidence for this specific algorithm.",
  "publishedFormula": {
    "formula": "H = L log₂(P); combos = P^L",
    "vars": [
      [
        "L",
        "Password length (4-64)"
      ],
      [
        "P",
        "Pool size (selected character sets)"
      ],
      [
        "H",
        "Entropy (bits)"
      ],
      [
        "t_c",
        "Crack time (seconds)"
      ]
    ],
    "note": "Generated in the browser with a cryptographic random-number API. Crack-time assumes 10 billion guesses per second and is only theoretical."
  },
  "externalEvidence": {
    "reviewedAt": "2026-09-26",
    "sources": [
      {
        "name": "MDN — Crypto.getRandomValues()",
        "url": "https://developer.mozilla.org/en-US/docs/Web/API/Crypto/getRandomValues"
      }
    ]
  },
  "contract": {
    "compute": "const upper='ABCDEFGHIJKLMNOPQRSTUVWXYZ',lower='abcdefghijklmnopqrstuvwxyz',digits='0123456789',symbols='!@#$%^&*()-_=+[]{};:.<>?/'; let pool=lower; if(includeUppercase==='yes')pool+=upper; if(includeDigits==='yes')pool+=digits; if(includeSymbols==='yes')pool+=symbols; if(avoidAmbiguous==='yes')pool=pool.replace(/[Il1O0]/g,''); const len=Math.max(4,Math.min(64,Math.round(length))),many=Math.max(1,Math.min(50,Math.round(count))),entropy=len*Math.log2(pool.length),log10Combos=entropy*Math.log10(2),log10Seconds=entropy*Math.log10(2)-10; const strengthLabel=entropy<40?'Weak — suited to nothing important':entropy<60?'Reasonable — acceptable for low-risk accounts':entropy<80?'Strong — fine for most accounts':entropy<120?'Very strong':'Extreme — far beyond any practical attack',crackTime=log10Seconds<0?'instantly':log10Seconds<1.8?'seconds':log10Seconds<3.6?'under an hour':log10Seconds<5?'under a day':log10Seconds<7.5?'under a year':log10Seconds<10.5?'centuries':'longer than the age of the universe'; if(typeof window==='undefined')return {passwords:'—',poolSize:pool.length,entropyBits:entropy,combinations:'10^'+Math.round(log10Combos),strengthLabel,crackTime,note:'Passwords are generated only in the browser. Entropy and crack-time labels are theoretical estimates.'}; if(!globalThis.crypto||!crypto.getRandomValues)return {__error:'inputError'}; const out=[],bytes=new Uint32Array(len*many); crypto.getRandomValues(bytes); let k=0; for(let i=0;i<many;i++){let s='';for(let j=0;j<len;j++)s+=pool[bytes[k++]%pool.length];out.push(s);} return {passwords:out.join(', '),poolSize:pool.length,entropyBits:entropy,combinations:'10^'+Math.round(log10Combos),strengthLabel,crackTime,note:'Generated with the browser cryptographic random-number API. Entropy is theoretical; crack-time labels assume 10 billion guesses per second and do not model real attack conditions.'};",
    "inputs": [
      {
        "name": "length",
        "type": "number",
        "default": 16,
        "min": 4,
        "max": 64,
        "step": 1,
        "slider": true
      },
      {
        "name": "count",
        "type": "number",
        "default": 5,
        "min": 1,
        "max": 50,
        "step": 1,
        "slider": true
      },
      {
        "name": "includeUppercase",
        "type": "select",
        "default": "yes",
        "options": [
          {
            "value": "yes"
          },
          {
            "value": "no"
          }
        ]
      },
      {
        "name": "includeDigits",
        "type": "select",
        "default": "yes",
        "options": [
          {
            "value": "yes"
          },
          {
            "value": "no"
          }
        ]
      },
      {
        "name": "includeSymbols",
        "type": "select",
        "default": "yes",
        "options": [
          {
            "value": "yes"
          },
          {
            "value": "no"
          }
        ]
      },
      {
        "name": "avoidAmbiguous",
        "type": "select",
        "default": "yes",
        "options": [
          {
            "value": "yes"
          },
          {
            "value": "no"
          }
        ]
      }
    ],
    "outputs": [
      {
        "name": "passwords",
        "precision": 0,
        "unit": "",
        "type": "text",
        "display": "list",
        "separator": ", "
      },
      {
        "name": "poolSize",
        "precision": 0,
        "unit": ""
      },
      {
        "name": "entropyBits",
        "precision": 1,
        "unit": "bits"
      },
      {
        "name": "combinations",
        "precision": 0,
        "unit": "",
        "type": "text"
      },
      {
        "name": "strengthLabel",
        "precision": 0,
        "unit": "",
        "type": "text"
      },
      {
        "name": "crackTime",
        "precision": 0,
        "unit": "",
        "type": "text"
      },
      {
        "name": "note",
        "precision": 0,
        "unit": "",
        "type": "text"
      }
    ],
    "interpret": null
  }
}
