Gunnar

nordisk/holger-ui

proto/holger-account.proto

syntax = "proto3";

// holger.account.v1 — the repositories a person has here (2026-10-21, lane P4).
//
// The console is a thin layer: "My repositories" lists what this server says
// the signed-in person has, makes a private repository through this server,
// and shows the client setup this server writes for each one. The console
// keeps no owners, no permissions and no store; it renders these answers and
// the server's refusals as they are.
//
// Its own package, like holger.room.v1: holger.proto, and every downstream
// copy of it, does not move.
package holger.account.v1;

service AccountService {
  // The repositories the caller has here: the ones made for them (they are the
  // owner), and for an ADMIN of this server every repository but the estate's
  // system ones. Anonymous: none, with the words to sign in. Never refused.
  rpc MyRepositories(MyRepositoriesRequest) returns (MyRepositoriesReply);
  // ADMIN of this server. Make a PRIVATE repository the caller owns, in one of
  // the formats `MyRepositoriesReply.formats` names. Its reads need a
  // credential of this server from the first request. The name is taken once:
  // a name this server serves or has made is ALREADY_EXISTS; a name that is no
  // repository name is INVALID_ARGUMENT; a format this server makes none of is
  // FAILED_PRECONDITION. A twin, a growth or a read-only server refuses as
  // every admin change does. The repository rides the twin and comes back with
  // a recharge, as a sold one does.
  rpc CreateRepository(CreateRepositoryRequest) returns (CreateRepositoryReply);
  // **The owner grants or revokes** (lane P1): a named person (their name here, or their
  // subject) may read, or read and write, one owner-only repository; `none` revokes. Its
  // OWNER only, or an admin of this server; every change is audited. The person keeps no
  // role elsewhere from it.
  rpc Grant(GrantRequest) returns (GrantReply);
}

message GrantRequest {
  string repository = 1;
  // The person: their name on this server, or their subject (`passkey:…`).
  string person = 2;
  // `read`, `write` (read and write) or `none` (revoke).
  string access = 3;
}

message GrantReply {
  string repository = 1;
  // Every grant the repository holds now.
  repeated RepositoryGrant grants = 2;
  // One sentence for the console.
  string words = 3;
}

message RepositoryGrant {
  // The person's name here (their subject when no name is known).
  string person = 1;
  string subject = 2;
  // `read` or `write`.
  string access = 3;
}

message MyRepositoriesRequest {}

message MyRepositoriesReply {
  repeated MyRepository repositories = 1;
  // The formats CreateRepository makes here for this caller ("cargo", "pip",
  // "npm", "maven", "raw", "oci"). Empty: this caller makes none.
  repeated string formats = 2;
  // One sentence for the console to show as it is: who may make one, or why
  // nothing is listed.
  string words = 3;
}

message MyRepository {
  string name = 1;
  // The client's word for its format: "cargo", "pip", "npm", "maven", "raw",
  // "oci", or the server's own type name for any other.
  string format = 2;
  // Its reads need a credential of this server.
  bool private = 3;
  // The principal it was made for; empty for the estate's own repositories and
  // for a sale whose buyer has not registered yet.
  string owner = 4;
  // A door serves it now. False: it is made and is served from the next start
  // (`words` says why).
  bool served = 5;
  // The client setup for this repository, written by this server.
  repeated ClientSetup clients = 6;
  // One sentence for the console to show as it is.
  string words = 7;
  // ── Its plan, as this server holds it (the ONE shop's signed fact) ──
  // The plan's name; empty when nothing is bought for it (it is free).
  string plan = 8;
  // This server's word for where the plan stands: "free", "paid", "grace",
  // "suspended", "retention", or "lapsed" (paid until a date that has passed,
  // and no renewal has reached this server: uploads are refused until it is
  // paid again).
  string plan_state = 9;
  // Paid until, Unix milliseconds; 0 when the fact names no date.
  int64 paid_until_unix_ms = 10;
  // One sentence for the console to show as it is, about the plan.
  string plan_words = 11;
  // The people its owner granted (lane P1): shown to its owner and to an admin only.
  repeated RepositoryGrant grants = 12;
}

message ClientSetup {
  // "cargo", "pip", "npm", "maven", "curl", "docker".
  string client = 1;
  // Where the snippet goes ("~/.cargo/config.toml"), or "shell".
  string file = 2;
  // The text, with YOUR-TOKEN where a token from My tokens goes.
  string snippet = 3;
}

message CreateRepositoryRequest {
  string name = 1;
  // One of `MyRepositoriesReply.formats`.
  string format = 2;
}

message CreateRepositoryReply {
  MyRepository repository = 1;
  // One sentence for the console to show as it is.
  string words = 2;
}