{
  "openapi": "3.1.1",
  "info": {
    "title": "Password Entropy Race API",
    "version": "1.0.0",
    "summary": "Race 2–6 passwords or passphrases: entropy, pattern penalties and crack times.",
    "description": "Every endpoint performs the same calculation as the human page and returns inputs, result, units, assumptions, sources, warnings and computedAt. MCP endpoint: /mcp."
  },
  "servers": [
    {
      "url": "https://passwordentropyrace.usefulweb.site"
    }
  ],
  "paths": {
    "/api/v1/calculate": {
      "post": {
        "operationId": "calculate",
        "summary": "Race 2–6 passwords or passphrases: entropy, pattern penalties and crack times.",
        "description": "Compares passwords and passphrases side by side: naive charset entropy, a pattern-aware estimate that penalises common passwords, keyboard walks, repeats, sequences, dates and l33t speak, and the average time to crack each one at four attacker speeds. With JavaScript on, everything runs in your browser and nothing is sent.\n\nMethodology:\n- Charset entropy = length × log2(pool), where the pool adds 26 for lowercase, 26 for uppercase, 10 for digits, 33 for ASCII symbols/space and 100 for any other character used. This is the optimistic \"brute force\" number most meters show.\n- The pattern-aware estimate splits the password into the cheapest sequence of guessable pieces (dynamic programming, similar to zxcvbn but simpler): common passwords and words from a bundled list of about 600 entries (log2 of the rank, plus bits for capitalisation, reversal and each l33t substitution), other pronounceable letter runs (assumed to be in a 100,000-word attacker dictionary, about 16.6 bits), keyboard walks on QWERTY (start key, length and turns), repeats, alphabetical/numeric sequences, years 1900–2039 and calendar dates. Word separators (space, dash, underscore, dot) cost 2 bits; remaining characters cost log2(pool) each; combining k pieces (separators not counted) adds log2(k!) bits.\n- The reported entropy is the smaller of the two numbers. Crack time is the average (half the search space, 2^(bits−1) guesses) divided by the attacker speed: 100 guesses/hour (online, throttled), 10 guesses/s (online), 10⁴ guesses/s (offline, slow hash such as bcrypt) and 10¹⁰ guesses/s (offline, fast unsalted hash on GPUs) — the scenario speeds popularised by zxcvbn.\n- The race winner is the highest pattern-aware entropy; results within 1 bit count as a photo finish.\n- Privacy: the same JavaScript file runs in your browser (form submission is intercepted, nothing is sent) and on the server (only when JavaScript is off, or via the API/MCP). Results show passwords masked; detected pattern pieces are listed so you can see why a password is weak.\n\nAn estimate for comparison and learning, not a guarantee. Real attackers use lists with billions of leaked passwords and smarter rules; never reuse passwords and prefer a password manager.",
        "tags": [
          "fun"
        ],
        "x-mcp-tool": "password_entropy_race",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PasswordEntropyRaceInput"
              },
              "examples": {
                "example1": {
                  "summary": "Classic password vs passphrase",
                  "value": {
                    "passwords": "P@ssw0rd2024\ncorrect horse battery staple\nTr0ub4dor&3"
                  }
                },
                "example2": {
                  "summary": "Keyboard walk vs random",
                  "value": {
                    "passwords": "qwertyuiop\n1qaz2wsx3edc\nk8#Vq2!mZr7w"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Calculation result envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PasswordEntropyRaceResult"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Invalid input (see errors[]).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (see Retry-After).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/tool": {
      "get": {
        "operationId": "getToolDefinition",
        "summary": "Inputs, output schema and examples for this tool",
        "responses": {
          "200": {
            "description": "Tool definition.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Problem": {
        "type": "object",
        "description": "RFC 9457 problem details. 422 responses include per-field `errors`.",
        "required": [
          "type",
          "title",
          "status",
          "detail"
        ],
        "properties": {
          "type": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "status": {
            "type": "integer"
          },
          "detail": {
            "type": "string"
          },
          "code": {
            "type": "string"
          },
          "errors": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "field",
                "message"
              ],
              "properties": {
                "field": {
                  "type": "string"
                },
                "message": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "PasswordEntropyRaceInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "passwords": {
            "title": "Passwords to compare (one per line)",
            "description": "Between 2 and 6 lines, up to 128 characters each. Do not paste passwords you really use: use look-alikes with the same structure. With JavaScript on, the comparison runs in your browser only; without JavaScript (and via the API/MCP) the text is sent to the server, used for the calculation and not stored.",
            "type": "string",
            "maxLength": 780
          }
        },
        "required": [
          "passwords"
        ]
      },
      "PasswordEntropyRaceResult": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "tool",
          "computedAt",
          "inputs",
          "summary",
          "result",
          "units",
          "assumptions",
          "sources",
          "warnings",
          "presentation",
          "links"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "const": "1.0"
          },
          "tool": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "name",
              "version"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "version": {
                "type": "string"
              }
            }
          },
          "computedAt": {
            "type": "string",
            "format": "date-time"
          },
          "inputs": {
            "type": "object",
            "description": "Normalised inputs actually used, including applied defaults."
          },
          "summary": {
            "type": "string"
          },
          "result": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "entries",
              "ranking",
              "winner",
              "tie",
              "scenarios",
              "wordlistSize"
            ],
            "properties": {
              "entries": {
                "type": "array",
                "items": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "label",
                    "masked",
                    "length",
                    "pool",
                    "charClasses",
                    "charsetBits",
                    "patternBits",
                    "structureBits",
                    "patterns",
                    "crackSeconds",
                    "crackTime",
                    "rating"
                  ],
                  "properties": {
                    "label": {
                      "type": "string"
                    },
                    "masked": {
                      "type": "string",
                      "description": "First and last character only."
                    },
                    "length": {
                      "type": "integer"
                    },
                    "pool": {
                      "type": "integer",
                      "description": "Character pool size implied by the character classes used."
                    },
                    "charClasses": {
                      "type": "array",
                      "items": {
                        "type": "string",
                        "enum": [
                          "digit",
                          "lower",
                          "other",
                          "symbol",
                          "upper"
                        ]
                      }
                    },
                    "charsetBits": {
                      "type": "number"
                    },
                    "patternBits": {
                      "type": "number"
                    },
                    "structureBits": {
                      "type": "number"
                    },
                    "patterns": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "additionalProperties": false,
                        "required": [
                          "kind",
                          "token",
                          "bits",
                          "note"
                        ],
                        "properties": {
                          "kind": {
                            "type": "string",
                            "enum": [
                              "dictionary",
                              "reversed",
                              "l33t",
                              "sequence",
                              "repeat",
                              "keyboard",
                              "date",
                              "separator",
                              "wordlike",
                              "bruteforce"
                            ]
                          },
                          "token": {
                            "type": "string"
                          },
                          "bits": {
                            "type": "number"
                          },
                          "note": {
                            "type": "string"
                          }
                        }
                      }
                    },
                    "crackSeconds": {
                      "type": "object",
                      "additionalProperties": {
                        "type": "number"
                      }
                    },
                    "crackTime": {
                      "type": "object",
                      "additionalProperties": {
                        "type": "string"
                      }
                    },
                    "rating": {
                      "type": "string",
                      "enum": [
                        "very weak",
                        "weak",
                        "reasonable",
                        "strong",
                        "very strong"
                      ]
                    }
                  }
                }
              },
              "ranking": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "winner": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "tie": {
                "type": "boolean"
              },
              "scenarios": {
                "type": "array",
                "items": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "id",
                    "label",
                    "guessesPerSecond"
                  ],
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "label": {
                      "type": "string"
                    },
                    "guessesPerSecond": {
                      "type": "number"
                    }
                  }
                }
              },
              "wordlistSize": {
                "type": "integer"
              }
            }
          },
          "units": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "assumptions": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "sources": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "title"
              ],
              "properties": {
                "title": {
                  "type": "string"
                },
                "url": {
                  "type": "string"
                },
                "publisher": {
                  "type": "string"
                },
                "retrievedAt": {
                  "type": "string"
                },
                "note": {
                  "type": "string"
                }
              }
            }
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "presentation": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Display blocks used by the human page (same data)."
          },
          "links": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "human",
              "api",
              "openapi",
              "methodology"
            ],
            "properties": {
              "human": {
                "type": "string",
                "format": "uri"
              },
              "api": {
                "type": "string",
                "format": "uri"
              },
              "openapi": {
                "type": "string",
                "format": "uri"
              },
              "methodology": {
                "type": "string",
                "format": "uri"
              }
            }
          }
        }
      }
    }
  }
}