Skip to content

refactor(proto): separate persistence-only messages from public API packages #3053

Description

@drew

User Story

As an API maintainer, I want database storage messages separated from public RPC contracts, so that public API evolution and storage migration can be reviewed and versioned independently.

Problem Statement

Persistence decodes database payloads directly into protobuf messages, including types also returned by public RPCs. Several messages named Stored* live inside the public openshell.v1 package even though they are not used in RPC signatures. Generated clients and descriptor consumers therefore see storage-only types, while changes to shared public/persisted types also become database migrations.

Impact / Why This Matters

The beta compatibility promise would accidentally include internal storage messages. A public field cleanup can break stored data, and a storage-only change can appear to break the public package. Maintainers cannot reason about wire compatibility and durable-data compatibility as separate concerns.

Proposed Design

Move storage-only messages into an explicitly internal, versioned storage protobuf package and exclude them from public SDK exports where practical. For resource messages used both on the wire and in persistence, decide whether to introduce internal storage twins with explicit conversion at the persistence boundary.

If storage twins are deferred, document which public messages are also durable storage formats, permanently prohibit unsafe tag reuse, and require migration review for every change to those messages.

Acceptance Criteria

  • Every persisted protobuf message and every public RPC message is inventoried, including overlaps.
  • All Stored* messages move out of public API packages without changing their durable wire encoding.
  • Generated public SDK surfaces no longer advertise storage-only types.
  • A reviewed decision is recorded for each dual-purpose public/persisted resource.
  • If storage twins are introduced, conversions deep-copy data and preserve unknown/legacy fields as required.
  • Existing databases upgrade without data loss and have regression fixtures from earlier versions.
  • Compatibility policy distinguishes public wire changes from storage migrations.
  • Tests fail when a storage-format change lacks the required migration handling.

Alternatives Considered

Leave dual-purpose types in place and rely on field-number discipline. This is the lowest-cost option but permanently couples every API change to storage. Move only the pure Stored* messages. This is a useful first step and may be selected if storage twins are not justified before 0.1.0.

Agent Investigation

crates/openshell-server/src/persistence/mod.rs decodes stored payloads into protobuf messages. Multiple Stored* declarations remain in proto/openshell.proto, alongside resource messages used both for persistence and public responses.

Related: #2565. Source audit: https://gist.github.com/mrunalp/e80942c1544a0225ee588796a41ab30b.

Activity

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

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions