Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
- **Breaking changes:** if work would break what people have saved or exported beyond what the saved-format rule covers, stop and report what breaks and the options before building a migration.
- **Shared code:** keep behaviour in its app until a second app needs it, then move it to `packages/` rather than copying it.
- **Budgets:** before raising an app's `jsBudget`, measure what grew and say why in the commit.
- **Docs:** reference docs describe what is built; intentions go in `docs/plan/`. Markdown links resolve from the folder of the file holding them; `bun run links` (part of `check`) fails on a missing target.
- **Docs:** each app is described in `docs/apps/`. An app that is not built yet says so at the top, and that file is its design. On a built app, ideas that are not built go under Later; when one ships, write it into the description and remove it from Later. When a description and the code disagree, the code is right and the description is fixed. The shared editor is `docs/editor.md`. Markdown links resolve from the folder of the file holding them; `bun run links` (part of `check`) fails on a missing target.

## Project conventions

Expand Down
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,4 @@ An app that is not released yet has no saved documents to carry forward. When it

## Docs

Reference docs describe the code as it is. When a doc and the code disagree, fix the doc. Intent that is not built yet goes in [`docs/plan`](./docs/plan).
Each app is described in [`docs/apps`](./docs/apps). When a description and the code disagree, fix the description. An app that is not built yet says so at the top. On a built app, an idea that is not built yet goes under Later in that file.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ send anonymous page-view analytics to Cloudflare when you are online.

The [site](https://peterhewat.github.io/Tools/) lists the tools and their capabilities.
The [catalog](packages/catalog/src/index.ts) is the source for that list.
The [roadmap](docs/README.md) shows each app, its place in line, and a link to its description.

## Running it

Expand Down Expand Up @@ -65,7 +66,7 @@ packages/
json-core/ source-preserving JSON parser and formatter shared by JSON and JWT
tsconfig/ shared TypeScript config
scripts/ build and scaffold scripts
docs/ decisions, reference and plans
docs/ decisions, and a description of each app
```

Each app is TypeScript built by Vite into plain static files, with **no runtime framework**.
Expand Down
85 changes: 58 additions & 27 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -1,27 +1,58 @@
# Docs

Four kinds of document. The kind tells you how much to trust it, and when it gets deleted.

| Kind | Meaning |
| ------------- | --------------------------------------------------------------------- |
| **Decision** | Settled once accepted; change it with a new ADR. |
| **Reference** | How the code works today. If it disagrees with the code, it is wrong. |
| **Plan** | Intent. Nothing here is built yet. |
| **Work log** | An open investigation. Deleted when the work lands. |

| Document | Kind | Read it when |
| -------------------------------------------------------------------------------- | --------- | ----------------------------------------------------------------- |
| [adr/001-static-apps-no-framework.md](adr/001-static-apps-no-framework.md) | Decision | Tempted to add a framework or a runtime dependency |
| [adr/002-catalog-drives-the-site.md](adr/002-catalog-drives-the-site.md) | Decision | Adding an app, or changing the deploy path |
| [adr/003-codemirror-for-code-editing.md](adr/003-codemirror-for-code-editing.md) | Decision | Editing or showing code in an app, or adding a CodeMirror package |
| [svg-spec.md](svg-spec.md) | Reference | Working on SVG |
| [plan/app-ideas.md](plan/app-ideas.md) | Plan | Browsing or prioritizing future Tools apps |
| [plan/code-editor.md](plan/code-editor.md) | Plan | YAML in, a Diff app on the shared editor |

## Conventions

- **Reference docs describe the present tense only.** Aspirations belong in a plan.
- **No history in docs.** Say what is and what is intended, not what was tried, renamed or
discarded along the way — that is what git history is for. Scope a plan with "out of scope",
not with a list of abandoned ideas.
- **When a doc and the code disagree, the code wins** — then fix the doc.
# Roadmap

One overview of the apps. Each name links to a description: what the app does, or the design when
it is not built yet. On a built app, ideas that are not built are appended under Later. Status in
this table is the release or the proposed place in line, not a promise that a Later item will ship.

## Apps

**Stable**, **Beta** and **Experiment** are catalog release statuses; an unlisted experiment
may not appear on the public index. **Next** is the next proposed new app, not work already
under way; **Planned** is a later proposal. Future rows are ordered by build priority.

| App | Status | Purpose |
| -------------------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [SVG](apps/svg.md) | Stable | Trace images with a Bézier pen and shapes; combine, align, group and fill with gradients. A searchable library of drawings, exported as clean SVG or PNG. |
| [JSON](apps/json.md) | Stable | Format, minify, fold and validate JSON, and fix almost-JSON. Convert it to YAML, CSV, TypeScript types or a JSON Schema. |
| [JWT](apps/jwt.md) | Stable | Decode, encode and verify JSON Web Tokens with HMAC, RSA, ECDSA and Ed25519. |
| [Codes](apps/codes.md) | Stable | QR codes for links, Wi-Fi, contacts, events and more, with your colours, shapes, logo and frame; plus Code 128, EAN-13 and UPC-A barcodes. |
| [Codec](apps/codec.md) | Stable | Convert UTF-8 text, Base64, Base64url, URL components and hex, with every byte visible. |
| [Digests](apps/digests.md) | Stable | Hash text and files with every SHA at once, check an expected digest, and compute HMAC. |
| [Time](apps/time.md) | Next | Convert timestamps, compare availability and measure durations. |
| [Diff](apps/diff.md) | Planned | Compare two texts or JSON documents, with clear changes and navigation. |
| [Certs](apps/certs.md) | Planned | Inspect X.509 certificates, chains and related data. |
| [Notes](apps/notes.md) | Planned | Write Markdown and organise a small local collection of notes. |
| [Magnet](apps/magnet.md) | Planned | Inspect torrent metadata and convert infohashes and magnet links. |

## Shared groundwork and boundaries

- `@tools/ui` supplies themes, headers, panels, menus, editor cards, copy/download helpers
and app build wiring.
- `@tools/editor` supplies CodeMirror behind one API; see the
[editor description](editor.md) and [ADR 003](adr/003-codemirror-for-code-editing.md).
- `@tools/bytes` supplies strict UTF-8, hex, Base64/Base64url and Web Crypto helpers.
- `@tools/json-core` supplies source-preserving JSON parsing and formatting.

Core features run locally, offline and from static hosting, under
[ADR 001](adr/001-static-apps-no-framework.md). Certs' optional revocation relay requires
a separate design decision. Keep a feature when it helps complete the same task with the
same input; give each app an explicit scope boundary. Drawing features belong in SVG.
Cutting and plotting tools serve a different workflow; palettes, gradients and sprite
slicing are outside the roadmap until a concrete need appears.

Names stay short and tool-like; slugs are lowercase kebab-case. New apps are scaffolded
with `bun run new-app`. Shared logic moves to `packages/` when a second app needs it.
Use existing shared controls, respect saved-format rules and measure bundle growth before
raising a budget.

## Maintaining this roadmap

Edit this table when priorities or release statuses change. Future row order is the proposed
build order. The [catalog](../packages/catalog/src/index.ts) controls which apps are built and
their release statuses; this page is the human-readable overview. Do not repeat status or
priority in an app file.

An app file describes what is built. When it and the code disagree, the code is right and the
file is fixed. Append an idea that is not built under Later; when it ships, write it into the
description and remove it from Later. An app that is not built yet says so at the top, and the
file is its design: append further ideas there.
2 changes: 1 addition & 1 deletion docs/adr/003-codemirror-for-code-editing.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
the shipped files keep working for years whatever happens to the toolchain.

Several apps edit or show code: the JSON app's text and its converted views, the SVG app's live
source panel, and planned apps such as Diff, JWT and Icon Check. What they need is an IDE's
source panel, JWT's decoded JSON, and planned apps such as Diff. What they need is an IDE's
editor: folding, bracket matching, auto-closing, indent on Enter, go to line, selection by
syntax, errors in place, large documents drawn only where they are on screen, plus the parts
that make an editor work for everyone: IME composition, phone keyboards and selection handles,
Expand Down
92 changes: 92 additions & 0 deletions docs/apps/certs.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# Certs

Not built yet. Status and build order are on the [roadmap](../README.md).

Name: **Certs**. Slug: `certs`. Descriptive title: **X.509 Certificate Inspector**.

The short name fits the developer audience and the site's other tool names. The descriptive
title makes the app's purpose clear; related certificate material can fit as focused extensions.

Purpose: paste a PEM or drop a DER certificate and see readable details beside it, without
sending it to a server for basic inspection.

Initial scope:

- Subject, issuer, serial number and validity dates.
- Subject alternative names, key type and size, signature algorithm and fingerprints.
- Extensions, including constraints, key usages, OCSP locations and CRL distribution points.
- Multiple certificate PEM blocks, with issuer/subject relationships shown as a potential
chain. A displayed relationship is not itself proof of a verified chain.
- Clear parsing errors and individually labelled checks, rather than one ambiguous
"valid" badge.

Useful extensions: compare certificates, check a hostname against the certificate's names,
check validity at a chosen time, compare expected fingerprints, and verify signatures
using supplied issuer certificates where supported.

Keep parsing, time checks, hostname matching, signature verification, chain trust and
revocation as separate results. A successful check does not imply all the others passed.
Building a complete trust engine equivalent to a browser, with a maintained root store
and its policies, is beyond the proposed inspector's scope.

## Related inputs and optional CSR inspection

- **Certificate chains:** a certificate and its issuing intermediate certificates, connecting
it towards a root. Display supplied relationships in the initial inspector; verifying
signatures and establishing trust are separate checks, and a supplied root is not
automatically trusted.
- **Certificate signing requests (CSRs):** a public key and requested identity information,
signed by the applicant and submitted to an issuer before a certificate is issued.
Optional later inspection could accept PEM or DER requests, show the subject, public key
and requested extensions such as alternative names, and check the request's signature.
A verified request signature is not an issuer's endorsement of the requested identity;
the issuer may change or reject requested fields. See [PKCS #10](https://www.rfc-editor.org/rfc/rfc2986.html).
- **Revocation data:** signed OCSP responses and certificate revocation lists (CRLs), used
to check revocation within their stated scope and time. Inspection and verification of
imported data can work locally, as described below.

CSR inspection is an optional extension, not a launch requirement. CSR generation,
private-key management and certificate issuance are outside this inspector's scope.

## Revocation: OCSP and CRLs

An OCSP URL in a certificate identifies a responder; it does not make a complete check
possible from the certificate alone. Constructing the request needs issuer name and key
information. A usable result requires verification of the response signature, authorised
signer, certificate identifier and freshness. OCSP returns good, revoked or unknown;
**good is a revocation status, not proof of overall certificate validity or trust**.
See [RFC 6960](https://www.rfc-editor.org/rfc/rfc6960.html).

There are three possible access paths:

| Path | What it enables | Constraint |
| ------------------------- | ---------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| Direct browser request | Online checks without a service operated by Tools. | Requires a responder reachable over HTTPS that permits the browser's cross-origin request; issuer retrieval can face the same restrictions. |
| Optional controlled relay | A server forwards the request; the browser verifies the signed response. | Adds hosting, maintenance and a service dependency; it is an architectural choice, not part of the offline core. |
| Imported response | Generate a ready-to-copy OpenSSL command and let the person import its response for inspection and verification. | Keeps the app static, with a manual external retrieval step and supplied issuer material. |

There is no general webpage-only bypass for a responder that blocks access. `fetch` with
`no-cors` gives an opaque response that JavaScript cannot read. HTTP requests from an HTTPS
page can also be blocked as mixed content. See
[MDN's CORS explanation and proxy alternative](https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CORS/Errors)
and [mixed-content rules](https://developer.mozilla.org/en-US/docs/Web/Security/Defenses/Mixed_content).

If a relay is pursued, restrict it to the intended certificate-checking operations, validate
destinations including redirects, prevent access to private networks, and bound request and
response sizes and timeouts. It must not become an arbitrary URL-fetching proxy. Make online
requests an explicit action; offline inspection remains useful when the relay is unavailable.

Frame the feature as revocation checking, rather than OCSP alone. Let's Encrypt ended its
OCSP service on 6 August 2025 and publishes revocation information through CRLs instead
([announcement](https://letsencrypt.org/2025/08/06/ocsp-service-has-reached-end-of-life)).
CRL retrieval encounters the same browser access limits. Importing a CRL is a possible local
extension, with explicit signature, freshness and coverage checks before making a claim.

Suggested progression: offline inspection first, then verification using supplied materials
and imported revocation data; decide separately whether reliable online checks justify a relay.

## Decisions before implementation

- Supported parsing and signature algorithms in the first release.
- Whether imported revocation checks are enough or an optional controlled relay is justified.
- Whether and when to add CSR inspection.
18 changes: 18 additions & 0 deletions docs/apps/codec.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Codec

Convert UTF-8 text, Base64, Base64url, URL components and hex, with every byte visible.

Every field holds the same bytes. Editing one rewrites the others; the field being edited is left
as written. While the text cannot be read yet, the error shows beside its name and the other fields
keep their last value.

UTF-8 is kept byte for byte, including Windows line breaks. Invisible characters are drawn. Bytes
that are not valid UTF-8 leave the text field empty. URL component is percent-encoding; a `+` is a
plus, not a space. Base64 accepts white space and optional padding; Base64url uses the unpadded
URL-safe alphabet. Hex accepts white space between complete byte pairs.

Digests, beside the text, opens the Digests app on the same bytes.

## Later

None yet.
22 changes: 22 additions & 0 deletions docs/apps/codes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Codes

QR codes for links, Wi-Fi, contacts, events and more, with your colours, shapes, logo and frame,
plus Code 128, EAN-13 and UPC-A barcodes.

Codes are made in the browser and never sent anywhere. They are static: once printed, what they
hold does not change.

A code can hold a link (`https://` is added when it is missing), plain text, an email, a call, an
SMS, a WhatsApp chat, Wi-Fi credentials, a vCard contact or a calendar event in the local time
zone. A barcode is Code 128 (printable ASCII, up to 80 characters), EAN-13 or UPC-A. Enter one
digit less and the check digit is calculated; enter all of them and it is checked. A Wi-Fi password
is readable by anyone who scans the code.

Colours, shapes, a logo and a frame change how a QR code looks. A barcode takes the colours only.
Every colour has an opacity; a background at 0% is transparent. A logo can cover the middle, with
error correction raised so the code can still scan. Download is SVG or PNG at about 512, 1024 or
2048 pixels wide, with the clear border scanners need.

## Later

None yet.
Loading
Loading