Skip to content

v0.3.0

Pre-release
Pre-release

Choose a tag to compare

@mhaya mhaya released this 17 Sep 18:14
· 109 commits to main since this release

The release in which a minted name can still be deleted, until it is published.
There was no delete anywhere before — this is a system that declares NR (never
reassign). But NR binds the names you have handed out, not a name at the instant
it is minted. Numbering a draft object ahead of time is ordinary practice, and when that
registration is called off, leaving a number nothing points at in the ledger forever
is not what keeping the promise looks like.

The boundary is a single Ark.published_at, and it is one-way: once published, it
cannot be deleted — except through POST /api/purge, which the registration authority's
operator may use and which always leaves a trace. The default is still "publish on
mint"
, so existing callers need no change, and every existing ARK migrates as
published.

Also fixed: alembic upgrade head did not run on SQLite. The exact steps in the
Quickstart had been stopping partway for three releases — because we had only ever
round-tripped on PostgreSQL
.

Added

  • An ARK can be deleted before it is published. There used to be no delete anywhere:
    declaring NR (no re-assignment) means resolution must never stop. But what NR
    binds are the names that went out into the world
    , not every name the moment it is
    minted. Reserving a number for an object that is still a draft is ordinary practice,
    and when such a deposit is abandoned, a number that points at nothing forever is
    not keeping the promise either.

    So Ark.published_at was added. null means "not published yet": the ARK does not
    resolve
    — the resolver answers as it does for a name it has never seen — and it
    can be deleted.

    • POST /api/mint with {"reserve": true} — mint it without publishing
    • POST /api/publish — publish it globally. From here on it cannot be deleted
    • POST /api/delete — withdraw one that was never published (a published one is 409)
    • arkhe ark publish / arkhe ark delete, and the same two actions on the ARK page of
      the admin interface
    • ARKHE_RESOLVE_UNPUBLISHED — a resolver inside a closed network resolves them

    The boundary only goes one way. Publishing cannot be undone; if it could, deleting
    before publication would mean nothing. The default still mints and publishes in one
    step
    , so existing callers need no change, and every existing ARK migrates as
    published.

    Whether it resolves is not decided by the ARK's state alone — it depends on which
    resolver is answering. A resolver started with ARKHE_RESOLVE_UNPUBLISHED=1, inside a
    closed network, does resolve reserved ARKs: a name minted in that network is
    worthless if the network's own resolver will not resolve it, which would defeat the
    point of giving closed objects the same shape of PID. The default does not, because
    ?info and ?? need no authentication and the safe side belongs in the default.

    A withdrawn name is not freed. It moves to withdrawn_name and is never assigned
    again, by minting or by import: a reserved identifier has usually already been handed
    to someone — that is what reserving is for — and re-using it would be
    indistinguishable, from outside, from breaking NR. Deletion also has its own scope,
    ark:delete; being able to mint and being able to withdraw are different decisions.

  • One narrow way out for a published ARK (POST /api/purge, arkhe ark purge,
    ark:purge).
    Everything here exists so that a published identifier keeps resolving, and a
    published ARK not being deletable
    is the centre of that. The door is nonetheless
    open because reality sometimes brings a demand that outweighs an identifier — a
    removal order, personal data that should never have been published, a mass ingest
    that went in wrong. Without a way out, someone ends up deleting rows straight from
    the database, and a deletion that leaves no trace is the worst kind.

    So it is not made impossible; it is made narrow and recorded:

    • the registration authority's operator only (ark:purge and
      authority=system) — never a NAAN administrator, never an organisation;
    • a reason is required — a purge that leaves nothing behind is the same as one
      that never happened;
    • confirm must repeat the ARK, so a script walking a list cannot empty the
      ledger by accident;
    • the name is not freed — it moves to withdrawn_name and is never assigned
      again;
    • the whole thing is audited.

    The last two are what survive. The target and the description go, but the name can
    never come to mean something else
    : a stale reference gets 404, never a different
    object. NR holds; what breaks is "keeps resolving".

    The ORM enforces it too. A published row cannot be deleted unless the session has
    named that one ARK as the one being purged
    (models.sanctioned_purge). A global
    flag would get left raised, and you would find out only after a row that must not
    disappear had disappeared.

Changed

  • An invariant is stated more narrowly. "An ARK is never deleted" is now "a
    published ARK is never deleted". Nothing that was protected has been given up — a
    name that went out still cannot go — but the promise has been rewritten to say exactly
    what it covers, and the versioning
    policy
    was updated to match.

Fixed

  • alembic upgrade head did not run on SQLite. The steps written in the Quickstart
    (ARKHE_DATABASE_URL=sqlite:///…, then alembic upgrade head) stopped at the third
    migration with NotImplementedError. SQLite has no ALTER that adds or drops a
    constraint
    , so create_foreign_key and drop_constraint have to go through batch
    mode — the copy-and-move strategy — and a partial index's where clause has to be
    sa.text() rather than a bare string.

    It had been broken for three releases. Round-tripping only against PostgreSQL hid
    it
    : that stays the authoritative check, but the steps the documentation gives have
    to be known to work
    on their own. tests/test_migrations.py now runs
    upgrade → downgrade base → upgrade on SQLite and compares the migrated schema's
    tables and columns against what the models declare.

    PostgreSQL behaviour is unchanged — batch_alter_table emits the same plain ALTER
    there. The widening migration still does nothing on SQLite, which does not enforce
    varchar lengths; rebuilding tables that carry primary and foreign keys to change a
    number SQLite ignores is not worth the risk (alembic check against SQLite reports
    exactly that difference).

  • The example ARKs in the documentation were not identifiers this ledger can mint.
    The reference and concept pages showed ark:99999/x9abc — a three-character blade, no
    check digit, and an a, which betanumeric does not contain. Only the walkthrough
    showed a real one (ark:99999/c7w545sj4z5), so the pages disagreed with each other
    about how long a name is. Readers learn the shape from the examples, and the shape
    being taught was one mint cannot produce.

    Every example is now a shoulder plus eight betanumeric characters plus a check digit,
    computed with the implementation's own noid_check_digit. ark:99999/x9tn1qkq2g7 —
    the name the walkthrough actually mints — now runs through the whole site, and the
    mistranscription example is one character away from it (…g8), so why ARKHE-1403 is
    a different answer from "no such ARK" can be read off the example itself. The examples
    inside the OpenAPI descriptions moved with them.

    A test holds the line (test_docs): every ark:99999/… in the documentation and
    in the generated OpenAPI must be betanumeric and must verify. The two deliberately
    wrong ones are listed with the reason each exists, and the test also checks that they
    still fail and are still in the documentation — a stale allowance cannot open a hole
    quietly.


日本語の変更履歴
· Full Changelog: v0.2.0...v0.3.0