Skip to content

Optional service block in the DID Document + Capability declaration via SchemaRecord #28

Description

@Jenziner

Summary

This issue proposes two related additions to RWP:

  1. An optional service block in the Record DID Document (§4.3), following the service property defined in [DID-CORE], allowing a Record's resolver to declare additional, optional access channels beyond the mandatory recordEndpoint.
  2. A mechanism, defined at the SchemaRecord level (§3.3, §schema-record), for declaring which functions/operations a Record Type — and therefore its resolver — supports (e.g. retrieve, compare, validate, publish). This declaration is what populates the service block above.

Both points belong together: the service block is the mechanism in the DID Document; the SchemaRecord-level declaration is the source of truth for what capabilities a given Record Type actually offers.

Motivation

RWP currently requires every Record DID Document to expose exactly one mandatory access channel: recordEndpoint, which MUST return the full Record. There is currently no normative way for a resolver to advertise additional ways of interacting with a Record — for example:

  • a restricted/public view of the Record for callers without full access rights,
  • a machine-readable listing of operations a Record (or its Record Type) supports, so that automated systems (including AI agents, e.g. via MCP) can discover what they are allowed to do with a Record before attempting to do it,
  • alternative retrieval or processing channels scoped by access level.

This aligns with the broader idea — discussed on public-credentials@w3.org (Michael Herman, Vikas Malhotra, Sept 2026, "What if: Data had DIDs (and a DID Document)?") — that instead of always pulling/pushing data to a processor, processing actions could be described in the DID Document itself and sent to the data. RWP is already structurally close to this: every Record has a DID, and every Record Type is governed by a SchemaRecord. Declaring supported functions per Record Type, and exposing that through the DID Document, is a natural extension of the existing model, not a new architectural concept.

Proposal

1. Optional service block (§4.3, DID Document)

Add service as an OPTIONAL field to the DID Document schema:

{
  "@context": "https://www.w3.org/ns/did/v1",
  "id": "did:rwp:bern.ch:f47ac10b-58cc-4372-a567-0e02b2c3d479",
  "recordEndpoint": "https://records.bern.ch/api/v1/records/f47ac10b",
  "created": "2026-05-31T14:00:00Z",
  "updated": "2026-05-31T14:00:00Z",
  "currentVersion": "sha256:e3b0c44298fc1c149afb...",
  "controller": "did:rwp:bern.ch:controller-001",
  "verificationMethod": [ ... ],
  "service": [
    {
      "id": "did:rwp:bern.ch:f47ac10b-58cc-4372-a567-0e02b2c3d479#public",
      "type": "RWP-PublicRecordAccess",
      "serviceEndpoint": "https://records.bern.ch/api/v1/records/f47ac10b/public"
    },
    {
      "id": "did:rwp:bern.ch:f47ac10b-58cc-4372-a567-0e02b2c3d479#functions",
      "type": "RWP-FunctionRegistry",
      "serviceEndpoint": "https://records.bern.ch/api/v1/records/f47ac10b/functions"
    }
  ]
}

Notes:

  • recordEndpoint remains the only MUST field for retrieving the full Record. service is MAY: an implementation is not required to offer any additional channel.
  • §4.3 currently states: "The DID document MUST be updated when the physical storage location changes (recordEndpoint, updated, currentVersion). All other fields MUST NOT be changed." This rule needs to be extended to clarify whether/how changes to service entries are permitted (e.g. MAY be added, updated, or removed without constituting a new Record version, since they do not affect Record identity or content).
  • Each service entry follows the DID-CORE structure (id, type, serviceEndpoint). The type values are RWP-specific and would need to be defined/registered by this specification (see open question below).

2. Capability declaration at SchemaRecord level (§3.3 / schema-record.bs)

A SchemaRecord already defines a concrete Record Type and its schema/validation requirements. This proposal extends that responsibility: a SchemaRecord SHOULD be able to declare which functions/operations are applicable to Records of that type — for example retrieve, compare, validate, publish, spellcheck, wordcount, or Record-Type-specific operations.

This declaration is what a resolver uses to populate the RWP-FunctionRegistry service endpoint for any Record of that type. It keeps the source of truth at the type level (governed, versioned, auditable via SchemaRecord) rather than requiring every individual Record instance to redundantly declare the same capabilities.

Open questions to resolve during discussion:

  • Exact structure of the capability list within SchemaRecord (flat list of operation identifiers vs. richer objects with input/output description).
  • Whether capabilities are purely descriptive (informational, "this Record Type supports X") or normative (a conformant resolver MUST implement declared capabilities).

Open question: namespace for service.type values

Three options have been considered:

  1. Reuse existing conventions where applicable (e.g. DIDCommMessaging for a DIDComm-based channel). This has the advantage of interoperating with existing DID tooling, but DIDComm's type only advertises a messaging transport — it does not itself declare which operations/features are supported. That discovery happens later, at runtime, via DIDComm's Discover Features protocol, after a connection is established. This is not well suited to RWP's goal of exposing capabilities directly via resolver lookup, without a prior handshake.
  2. RWP-defined type values (e.g. RWP-FunctionRegistry, RWP-PublicRecordAccess, RWP-MCP), registered and defined normatively by this specification. This keeps capability discovery resolvable in a single lookup, consistent with RWP's existing resolver model.
  3. A new SystemRecord subtype, e.g. ServiceRecord, following the same pattern as SchemaRecord, MergeRecord, DeletionRecord, etc. This would be justified only if the capability declaration itself needs to be a fully versioned, hash-verifiable, independently addressable Record (with its own DID, integrity evidence, and lifecycle) — rather than a field carried inside SchemaRecord or the DID Document's service block.

Recommendation for discussion: start with option 2 (RWP-defined type values, populated from SchemaRecord), since it requires no new Record type and fits directly into the existing service mechanism of [DID-CORE]. Option 3 should be revisited only if capability declarations need independent versioning/integrity guarantees beyond what SchemaRecord already provides.

Relationship to Access Control

RWP already delegates authorization decisions to implementing systems (see chapters/access-control.bs) rather than mandating a specific access-control model. This proposal should explicitly cross-reference that chapter: a service entry (e.g. a restricted/public view, or a function registry) is a declaration of availability, not a grant of access. Actual authorization for invoking any declared service or function remains governed by the access-control rules already defined elsewhere in the specification. The two chapters should reference each other so implementers understand that exposing a service entry does not bypass access control.

Non-goals

  • This issue does not propose removing or changing the mandatory recordEndpoint field or its semantics.
  • This issue does not propose a specific transport protocol (DIDComm, MCP, plain REST, etc.) as mandatory. Any concrete protocol choice should remain implementation-specific, expressed via the type/serviceEndpoint pair.

References

  • [DID-CORE] Decentralized Identifiers (DIDs) v1.0/v1.1, W3C — service property.
  • RWP §4.3 DID Document, §3.3 Record taxonomy, chapters/schema-record.bs, chapters/access-control.bs.
  • Related public discussion: public-credentials@w3.org thread "What if: Data had DIDs (and a DID Document)?" (Sept 2026).

Activity

  1. Jenziner commented on Sep 9, 2026

    @Jenziner
    ContributorAuthor

    DIDComm Discover Features as related work

    Following up on feedback from the public-credentials@w3.org thread: DIDComm Messaging defines a Discover Features protocol (2.0 details) that lets one agent query another, at runtime, for which protocols/features it supports (discover-features/2.0/queries → discover-features/2.0/disclose).

    Worth noting for this discussion, but I don't think it's a substitute for the service/SchemaRecord proposal above — it solves a related but different problem:

    • Discover Features operates after a DIDComm connection is already established over a known channel. It doesn't help an agent decide, purely from resolving a Record's DID, what that Record supports — which is the core goal here (capability discovery at resolution time, without a prior handshake).
    • Disclosure is best-effort: an agent may simply not answer a given query. RWP resolvers, by contrast, are expected to behave deterministically (200/404/410).
    • It discovers protocol support between agents (e.g. "do you support protocol X v1.*"), not operations available on a specific Record/Record Type (retrieve, compare, validate, ...).

    So I'd frame this as complementary rather than an alternative: our service entry says "here's a DIDComm endpoint for this Record," and Discover Features could then be used after that connection is made, to further negotiate protocol-level details at runtime. It doesn't remove the need to expose capabilities directly via the DID Document/SchemaRecord at resolution time.

    Given that, I'd suggest we don't fold Discover Features into the normative RWP requirements here, but instead reference it in RWC as a related mechanism / design option for future runtime negotiation, once a service channel (e.g. DIDComm) is in place. Happy to add that pointer to RWC separately if there's agreement.

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

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions