Skip to content

Design: proving who publishes an app (signed manifests, publisher pinning, certified keys) #404

Description

@cuibonobo

Problem

With app installs (#359, #400), an appId is the manifest's own claim. Any key can present a manifest under com.example.notes:

  • Where that app isn't installed, approving the manifest gives the key the app's families.
  • Where it is installed, approving it links the key to the existing install. The key then holds the app's grants and may migrate its types, and the manifest's requests replace those of every key already linked.

Today the plan's newKey and linkedKeys show this, so the owner's review is the only check. apps.md § Who owns a family states this residual risk.

A design that was prototyped and taken out of #400

Commits 07ad8e8, e1232a7 and de4e587 on #400's branch implemented these pieces:

  • Signed manifests. A publisher DID signs canonical JSON (manifestPayload() / signManifest()).
  • Publisher pinning. A manifest under the same appId from any other publisher is refused while the install is live. After an uninstall, a new publisher may take the install up (publisherChanged), which unlinks the old keys.
  • Forward-only release. This stops an older signed manifest being replayed over a newer install.
  • Optional certified keys (certifyKey()). There is also a sticky keysCertified flag: once one key is certified, a new key must be too.
  • did:web publishers. They are verified through a verifyPublisher callback, and namespaceVerified is set when the domain, reversed, is the appId.

It was taken out because:

  • It's mostly trust on first use anyway. With a did:key publisher, the first manifest a stack sees under an appId gets pinned, whoever signed it.
  • Certificates prove less than they seem to. For an app on users' devices, a certificate proves only what the publisher's issuing service checked, which may be nothing.
  • Each layer added edge cases: the sticky flag surviving an uninstall, publisher rotation unlinking keys, and releases not carrying across a publisher change.
  • It overlaps identity.md § Deferred: key rotation. Tying a key to an identity that outlives it is the same problem.
  • It makes the simplest case heavier. Every app, including an in-process one, would need a publisher keypair.

Questions

  • Should this be designed together with key rotation, for example a stack-hosted DID method or a signed hand-off log?
  • Is did:web with a domain matching the appId the only case worth proving, with everything else left to owner review?
  • What should the plan show the owner so it's meaningful without signatures, for example a fingerprint of the key and where else it is installed?
  • Does upgrade protection (no other key can upgrade a live install) need signatures at all, or would pinning to the first linked key, with the owner approving every new key, be enough?

Activity

  1. added theissue type on Oct 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions