syntax = "proto3";

package galaxy.backend.push.v1;

option go_package = "galaxy/backend/proto/push/v1;pushv1";

// Push is the unidirectional control channel from backend to gateway.
//
// Gateway opens SubscribePush once at startup and keeps the stream open.
// Backend pushes two classes of events: ClientEvent (opaque payload that
// gateway signs and forwards to active client subscriptions) and
// SessionInvalidation (instructs gateway to close subscriptions and
// reject in-flight requests for the affected sessions).
//
// See backend/README.md §7 for the runtime contract.
service Push {
  rpc SubscribePush(GatewaySubscribeRequest) returns (stream PushEvent);
}

// GatewaySubscribeRequest opens a push subscription. gateway_client_id
// uniquely identifies the gateway instance; backend tracks one
// subscription per id and replaces an existing one on reconnect. cursor
// is the last consumed PushEvent.cursor; backend resumes from the next
// event when the cursor is still inside the freshness-window ring, or
// from a fresh point otherwise.
message GatewaySubscribeRequest {
  string gateway_client_id = 1;
  string cursor = 2;
}

// PushEvent is one server-pushed frame. Exactly one of the kind oneof
// fields is set. cursor is a monotonically increasing string assigned by
// backend at publish time; gateway persists the last cursor it processed
// so it can resume after reconnect.
message PushEvent {
  oneof kind {
    ClientEvent client_event = 1;
    SessionInvalidation session_invalidation = 2;
  }
  string cursor = 3;
}

// ClientEvent carries an opaque payload destined for one user_id and
// optionally one device_session_id (empty means fan-out to every active
// session of user_id). kind is the notification catalog kind from
// README §10. payload is the JSON encoding of the producer's payload
// map; gateway forwards the bytes inside the signed envelope without
// re-interpreting them.
//
// event_id, request_id and trace_id are correlation identifiers that
// gateway carries verbatim into the signed client envelope. event_id is
// stable per logical client-facing event (typically the route id of the
// notification route that produced the event). request_id and trace_id
// are optional and may be empty when the producer has no upstream
// correlation to attach.
message ClientEvent {
  string user_id = 1;
  string device_session_id = 2;
  string kind = 3;
  bytes payload = 4;
  string event_id = 5;
  string request_id = 6;
  string trace_id = 7;
}

// SessionInvalidation tells gateway to drop active subscriptions and
// reject in-flight authenticated requests bound to the affected
// sessions. user_id is always set; device_session_id narrows the
// invalidation to a single session, empty means revoke every session of
// user_id. reason is a free-form code logged by both sides.
message SessionInvalidation {
  string device_session_id = 1;
  string user_id = 2;
  string reason = 3;
}
