Metadata-Version: 2.4
Name: jidoseal
Version: 0.1.4
Summary: JidoSeal local scan runner: checks a folder's markdown frontmatter tiers on your own machine. Sends nothing over the network (vendor claim; see the jidoseal-mcp README).
Author-email: JidoSeal <support@jidoseal.com>
License: Proprietary
Project-URL: Homepage, https://jidoseal.com
Project-URL: Documentation, https://github.com/Jidoseal/jidoseal-mcp/blob/main/docs/tiers.md
Project-URL: MCP server, https://github.com/Jidoseal/jidoseal-mcp
Project-URL: Certificate registry, https://jidoseal.com/registry
Keywords: okf,knowledge-base,governance,markdown,frontmatter,local-first
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Documentation
Requires-Python: >=3.9
Description-Content-Type: text/markdown
Requires-Dist: PyYAML>=6.0

# JidoSeal CLI

Run JidoSeal's Self-Check over a folder of markdown, from your terminal, on your own machine.

```
JidoSeal states that a scan makes no network connection and sends no file, file name or
file content anywhere. The scan engine is proprietary, so this is JidoSeal's own claim,
not an independent audit. How to check it:
https://github.com/Jidoseal/jidoseal-mcp#what-leaves-your-machine
```

That is the first thing the command prints, on every run, before it reads a single file. It is
JidoSeal's own statement about its own proprietary engine, and it says so. The rest of this page
explains exactly what the tool does, what it does not do, and how to check the claim yourself.

---

## Install

**One-liner:**

```bash
pipx install jidoseal && jidoseal --root .
```

`pipx` is the recommended install because it puts JidoSeal in its own isolated environment, so
it can neither shadow nor be shadowed by anything else on your system. If you would rather use
pip, a virtual environment does the same job:

```bash
python3 -m venv ~/.venvs/jidoseal
~/.venvs/jidoseal/bin/pip install jidoseal
~/.venvs/jidoseal/bin/jidoseal --root /path/to/your/notes
```

**From a local wheel:**

```bash
python3 -m pip wheel . --no-deps -w dist-cli      # from a checkout of this repo
pipx install ./dist-cli/jidoseal-0.1.4-py3-none-any.whl
```

**From a checkout, with no install at all:**

```bash
python3 engine/local_runner.py --root /path/to/your/notes
```

Requires Python 3.9 or newer. Developed and exercised on 3.14.

---

## Usage

```bash
jidoseal --root /path/to/your/notes
```

```
JidoSeal states that a scan makes no network connection and sends no file, file name or
file content anywhere. The scan engine is proprietary, so this is JidoSeal's own claim,
not an independent audit. How to check it:
https://github.com/Jidoseal/jidoseal-mcp#what-leaves-your-machine

scanned 747 files in 1.42s on this machine
  Bronze  coverage: 98.1%
  Silver  coverage: 84.3%
  Gold    coverage: 61.0%
manifest: /path/to/your/notes/.jidoseal/manifest.json
progress: /path/to/your/notes/.jidoseal/progress.ndjson (run scan-20260918-143012-a1b2c3)
```

| Flag | What it does |
| --- | --- |
| `--root <dir>` | **Required.** The folder to scan. Every `*.md` beneath it, recursively. A folder holding a symlink that leads outside it is refused; see [Symlinks](#symlinks). |
| `--include-machine` | Ignore the machine/transient excludes your `.jidoseal/config.yaml` declares, and scan every `*.md` under `--root`. With no config file there is nothing to ignore, so the flag changes nothing. |
| `--version` | Print the version and exit. |
| `--help` | Print usage and exit. |

`python3 -m jidoseal --root <dir>` is identical to `jidoseal --root <dir>` — same entry point,
same output, same banner.

### A caution about `--include-machine`

**The certified path does not use it.** When you buy a certificate, JidoSeal scans with your
configured machine excludes applied — that is the corpus definition a certificate is issued
against. `--include-machine` is a diagnostic: useful for seeing your whole tree, but the coverage
percentages it prints are **not** the percentages your certificate would be based on. For a
number that matches, run without the flag.

### What it writes

Two files, both inside `<root>/.jidoseal/`, both on your machine:

- **`manifest.json`** — per file: a content hash, its current tier, which governance fields are
  present, and exactly what is missing for Silver and Gold. This is the same artifact, in the
  same format, that the certified engine path produces.
- **`progress.ndjson`** — an append-only progress log, one JSON object per line, flushed to disk
  as each file is scanned. Append-only on purpose: a new run adds to it rather than erasing the
  history of previous ones.

Nothing else is written, and nothing in your corpus is modified. This command **reads only**.

### Symlinks

A scan stays inside the folder you give it, and so do its writes.

- **A symlink that leads outside the folder** — to a file or to a directory — is never followed.
  The whole run is refused before anything is read or written, and the message lists every
  offending link. Remove them, or scan the folder they point to directly. The command exits
  with status 2, so a CI gate cannot mistake a refusal for a scan.
- **A symlinked `.jidoseal/`**, or a symlink in place of any record inside it, is refused the
  same way, wherever it points: every write the engine makes goes there.
- **A symlinked directory that stays inside the folder** is not descended into. What it points
  at is scanned where it actually lives, so each file is read once and a loop (a link to `..`)
  has nothing to expand.
- **A symlinked `.md` that stays inside the folder** is read once: not at all if its target is
  already scanned under its own name, otherwise once under the link's name.
- Hidden files and folders (names starting with `.`) are skipped, as they always were.

The same rule holds for every write an apply makes (`python3 -m okf_manifest --apply ...`):
each file named in the manifest, each snapshot and the audit log must resolve inside the folder,
or the apply is refused before anything is written.

Two limits, said plainly. A **hard link** is not a symlink: it is an ordinary file inside the
folder, and it is read like one. And a symlink created **during** a scan, after the check has
run, is not caught by it.

---

## Telling JidoSeal about your corpus — `.jidoseal/config.yaml`

**Out of the box, nothing is excluded and nothing is assumed.** Every `*.md` file under
`--root` is scanned, apart from OKF v0.2's two reserved filenames (`index.md` and `log.md`),
which the standard itself puts outside a concept corpus. No folder name is special, no owner is
invented, and no document type is inferred from a directory JidoSeal has never seen.

That is deliberate. A scanner that silently drops files because their path matched somebody
else's folder convention is reporting coverage over a corpus you did not choose — and the
number it shows you would be wrong in a way you cannot see.

If your corpus *does* have conventions, tell JidoSeal about them in an optional
`<root>/.jidoseal/config.yaml`. Every key is optional; leaving the file out is exactly the
default above. The config file itself is never part of the corpus (JidoSeal only ever reads
`*.md`), and it is read by the scan engine itself — so the CLI, the local UI and the
certificate-issuing path all see the same corpus definition.

```yaml
# Paths excluded as machine-generated or synced mirrors. Python regexes, matched against the
# path relative to --root. `--include-machine` ignores this list for one run.
machine_extra_excludes:
  - "^Scratch/"          # transient working notes
  - "/_drive/"           # a sync mirror — remediating it just fights the sync
  - "(?i)-autogen-"      # generated files, by filename

# Who owns a file when its frontmatter does not say. First match wins.
owner_map:
  - pattern: "^Accounts/"
    owner: "dept:finance"

# The owner for everything no rule above claims. With no `default_owner`, JidoSeal will not
# invent one — and an apply refuses rather than writing an audit line it cannot attribute.
default_owner: "human:jane-doe"

# Optional: the identity recorded on each audit-log line. Defaults to `default_owner`.
authority: "self:jane-doe"

# Extra `type` inference for your own folders, applied AFTER JidoSeal's built-in conventions
# (so a more specific built-in rule always still wins). Suggested at MEDIUM confidence, and
# never written without your explicit Apply.
propose_type_rules:
  - pattern: "^Accounts/"
    type: "finance-reference"
```

A malformed config is an error, not a shrug: an unknown key or a bad regex stops the run with
a message naming the file, rather than quietly scanning a different corpus than you described.

### Your config is recorded with the scan — and on the certificate

Every scan prints one more line:

```
config:   sha256:9f2c…c41d
```

That is a SHA-256 of your `.jidoseal/config.yaml`, exactly as `sha256sum` computes it — check it
yourself:

```bash
sha256sum .jidoseal/config.yaml
```

With no config file, the line reads `config:   absent:no-config-file`: running on the shipped
neutral defaults is recorded as such, rather than left blank.

Why it exists: this file decides which documents are in your corpus at all, so it is part of what
a tier means. A certificate is bound to a Merkle root of your files, which says WHICH files were
certified but not which definition selected them — so the same digest travels onto the
certificate, inside its signature. It records your corpus definition; it does not restrict it.
Nothing here leaves your machine on the free path, and the digest is one-way: it carries no
folder name, no path and no rule text.

---

## If your vault is not written in English

JidoSeal reads a vault in any of twelve languages — English, Chinese (Simplified and
Traditional), Spanish, Japanese, German, French, Portuguese (Brazil and Portugal), Korean,
Russian and Hindi — and every scan prints which one it decided on:

```
language: ja  (i18n table 1.0.0 sha256:711f2ff…6f3d1)
```

That digest is a SHA-256 of `i18n/frontmatter-i18n.json`, the table this build read, checkable
the same way the config digest is. It travels with the scan so a certificate can record which
table scored the corpus. It is **not** part of `config_hash`: that value stays the plain digest
of your own `.jidoseal/config.yaml` and nothing else, so `sha256sum` still verifies it.

**Keys stay English. Values are yours.** JidoSeal writes `title`, `description`, `status` and
the rest in English, because Obsidian's own search and every static-site generator expect them
there — renaming `tags` to `标签` is documented to break tag search. But it READS a key you have
already renamed: `標題`, `descripción`, `Beschreibung`, `заголовок` and the rest all count as the
fields they mean, so a vault that is complete is scored as complete instead of as empty. If a
field already exists under a name you chose, a value JidoSeal fills in goes under **your** key,
never a second English one beside it. `tags`, `aliases` and `cssclasses` are never touched at
all, renamed or not.

**Anything JidoSeal invents is in your language; anything it read from you is left alone.** A
title taken from your heading or your filename stays exactly as you wrote it. The fallback used
when there is nothing to read does not: it is `Untitled` in English and `タイトルなし` in
Japanese. Dates are the exception in every locale — always ISO 8601 with ordinary digits, never
localised, because that is the one format every tool downstream can read.

**Two things it cannot do, said plainly.** Script tells Japanese, Korean, Chinese, Russian and
Hindi apart; it cannot tell English, Spanish, German, French and Portuguese apart, because they
share an alphabet. For those five, JidoSeal only knows your language if a key is in it. And a
Chinese file with no character unique to Simplified or Traditional is read as Simplified unless
the rest of the vault says otherwise. Either can be settled outright by putting `lang: pt-PT`
(or any locale above) in a file's frontmatter — a declaration beats every inference.

An IME's full-width colon (`状態：安定`) is repaired before parsing rather than failing the
file, as is a colon left inside a quoted key (`"status:"`).

---

## What the tiers mean

Tiers are objective, deterministic and pass/fail. There is no judgement call and no model
involved anywhere in this path — a file either has a populated field or it does not, and the
same corpus scanned twice gives the same answer.

| Tier | Requires |
| --- | --- |
| **Bronze** | `type` |
| **Silver** | Bronze, plus `title`, `description`, `timestamp`, `owner` |
| **Gold** | Silver, plus `status`, `review_policy`, `reviewed_at`, `next_review_at` |

"Populated" means carrying real content. A bare `title:` with nothing after it earns nothing.

A file whose frontmatter fence exists but whose YAML will not parse is reported with
`frontmatter_ok: false`. Those gaps cannot be closed by adding fields — the block has to be
fixed by hand first.

---

## Honest scope — what JidoSeal does and does not certify

**It does:**

- Certify **presence and integrity of governance fields** — that the metadata your documents are
  supposed to carry is actually there and actually populated, measured the same way every time.
- Provide **hash provenance** — every file's content is hashed, so the manifest is bound to the
  exact bytes on disk at scan time, and a later change is detectable.

**It does not:**

- **Attribute which change broke a spec.** JidoSeal will tell you that a document's governance
  state is what it is now, and that its content hash differs from before. It does not diff your
  documents or identify which edit — by you, by an agent, by anything else — was responsible.
- **Judge whether your content is any good.** Tiers measure governance-field presence, not
  quality, accuracy or usefulness of what you wrote.
- **Read your prose.** The scan reads frontmatter to determine fields, and hashes file bytes.
  JidoSeal states that none of it is sent anywhere; see the next section for how to check.

---

## Exactly what leaves your machine

JidoSeal's answer is: nothing. That is JidoSeal's own claim about a proprietary engine, not the
result of an independent audit, so the command prints it in those words:

```
JidoSeal states that a scan makes no network connection and sends no file, file name or
file content anywhere. The scan engine is proprietary, so this is JidoSeal's own claim,
not an independent audit. How to check it:
https://github.com/Jidoseal/jidoseal-mcp#what-leaves-your-machine
```

Here is what it rests on:

- **The installed package does not contain networking code.** The wheel ships ten Python
  modules — the complete set the scan needs and nothing more. The engine's network-capable
  modules are not merely unused by this command; they are not in the distribution. You can
  confirm that yourself with `unzip -l` on the wheel, or by listing the installed files.
- **Its one dependency is PyYAML**, a YAML parser, used to read frontmatter. It has no network
  capability. See "Dependencies" below.
- **Static analysis checks it in JidoSeal's test suite** — the modules on this path are
  AST-inspected for any import of `socket`, `ssl`, `http`, `urllib`, `requests`, `httpx`,
  `aiohttp` and friends, and the check fails the build if one appears.
- **Runtime checks run it for real** — in the same suite, a full scan is executed inside a
  harness that raises on any outbound connection or DNS lookup, and the installed `jidoseal`
  command is run in a child process that is stopped on its first socket event. Neither fires.
- **Those tests are JidoSeal's own**, in a private repository. The public
  [`jidoseal-mcp` repository](https://github.com/Jidoseal/jidoseal-mcp#what-leaves-your-machine)
  runs this same engine under public tests that check the same boundary, and lists commands
  (`strace`, `bwrap --unshare-net`) you can run yourself.

If you would rather trust your own eyes than our test suite: run it with a network monitor on.
That is the point of a local tool.

Buying a certificate is a separate step that happens on jidoseal.com, not through this command.
This CLI has no purchase path, no account and no telemetry.

---

## Dependencies

One: **PyYAML**.

We would rather it were zero, and it is worth being precise about why it is not. JidoSeal's
frontmatter parser has always used PyYAML — it is the engine's single hard requirement, and the
certified engine path uses the very same parser. Replacing it would mean writing a second YAML
implementation, and a second implementation is a second set of edge cases: the day it disagrees
with the first about a quoted date or a multi-line string, the CLI and your certificate disagree
about your corpus. A pure parser with no network capability is a much better trade than a parity
risk in the thing being certified.

Everything else on the path is the Python standard library.

---

## Same engine as the certificate

This is the point of the CLI, so it is worth stating plainly: `jidoseal --root <dir>` runs the
**same code** the certified issuance path runs. Both call `okf_manifest.build_manifest()`; both
tier files through the same `tier_of()`; both hash content the same way. The CLI is not a
preview, a re-implementation or an approximation of the certified scoring — it is that scoring,
invoked locally.

The practical consequence: the tiers you see in your terminal are the tiers a certificate would
attest to, for the same corpus, scanned the same way (that is, without `--include-machine`).

---

## Using JidoSeal from Claude Code, Cursor, or another AI tool

If you work inside an AI coding tool, there is a second package that lets it run this same scan
for you — without you leaving the tool, and without your files leaving your machine:

```bash
pip install jidoseal-mcp
```

That installs a server speaking the **Model Context Protocol**, the standard those tools use to
call local programs. Point your host at the `jidoseal-mcp` command:

```bash
claude mcp add jidoseal -- jidoseal-mcp
```

…or, for Cursor and most other hosts, in their MCP config file (setup for Claude Desktop, GitHub
Copilot, OpenAI Codex, Gemini CLI, Zed, Cline, Continue and JetBrains AI Assistant is in the
[`jidoseal-mcp` README](https://github.com/Jidoseal/jidoseal-mcp#install-and-wire-it-into-your-tool)):

```json
{
  "mcpServers": {
    "jidoseal": {
      "command": "jidoseal-mcp"
    }
  }
}
```

Your assistant can then run the free Self-Check over a folder, explain which fields each file is
missing for the next tier, and — only if you ask it to — start a certification checkout. The
scan is the same local scan this CLI runs, on the same engine, producing the same tier and the
same Merkle root.

The statement about what leaves your machine is the same one, with the same status — an AI tool
making the call does not change who holds the data:

    JidoSeal states that a scan makes no network connection and sends no file, file name or
    file content anywhere. The scan engine is proprietary, so this is JidoSeal's own claim,
    not an independent audit. How to check it:
    https://github.com/Jidoseal/jidoseal-mcp#what-leaves-your-machine

The server also refuses a folder that holds a symlink leading outside it, before the engine runs.

The one thing that does send data is a purchase, and only if *you* choose one: the server's
checkout tool sends your company name, your name and your email, plus the tier, a 0–100 score,
the corpus's Merkle root, the scan's id and which Bronze price applies, to jidoseal.com, which
copies them into the Stripe Checkout session it creates, before any payment. Never file
contents, file names or paths.

---

## Changes

**0.1.4**

- **Symlinks are no longer followed out of the folder.** 0.1.3 found files with a recursive glob
  that followed symlinks, so a link inside the folder could pull outside files into a scan and
  its Merkle root, and a symlinked `.jidoseal/` redirected the scan's own writes. A scan, and
  every apply, now refuses such a folder before reading or writing anything, and lists the
  links; see [Symlinks](#symlinks). A symlinked directory inside the folder is no longer
  scanned twice.
- **Apply stays inside the folder.** Every file an apply writes, every snapshot, the audit log
  and a rollback's restores must resolve inside the folder; a manifest naming `../` paths is
  refused.
- **The no-network line is worded as JidoSeal's claim.** The banner, `--help` and the PyPI
  summary no longer state as a bare fact that nothing leaves your machine; they say it is
  JidoSeal's own statement about a proprietary engine, not independently audited, and where to
  check it.
- A `--root` whose path contains `[`, `*` or `?` is now scanned correctly.
- Project links (homepage, rubric, MCP server, certificate registry) on the PyPI page.
- Scoring is unchanged: a folder without symlinks gets the same manifest as under 0.1.3.
