Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

gRPC API

The proto contract lives at proto/infrastore/v1/store.proto and is compiled into infrastore-proto with tonic. The service is read-only — every write operation (add, remove, clear, compact) requires local filesystem access and is intentionally absent.

The association catalogs are absent too, reads included: no message or RPC covers supplemental_attribute_associations or parent_child_associations. Consumers of those tables work against a local Store.

  • Package: infrastore.v1
  • Service: CatalogStore

Methods

RPCRequestResponsePurpose
ListTimeSeriesListReqListRespList metadata matching a filter
GetTimeSeriesGetReqGetRespFetch one series' values
GetTimeSeriesKeysKeysReqKeysRespList keys for an owner
GetResolutionsResolutionsReqResolutionsRespDistinct resolutions present
GetCountsCountsReqCountsRespAggregate counts
GetForecastParametersForecastParamsReqForecastParamsRespHorizon, interval, count, resolution
HasTimeSeriesHasReqHasRespExistence check
VerifyIntegrityVerifyReqVerifyRespRecompute and compare stored hashes
ListKeysListKeysReqListKeysRespFull keys (opt. content hash) by filter
GetMetadataKeyReqTimeSeriesMetadataMetadata for one key
BulkReadBulkReadReqBulkReadRespFetch many series at once (opt. range)
GetDetailedCountsEmptyReqDetailedCountsRespDistinct owners/arrays per kind
GetCountsByTypeEmptyReqCountsByTypeRespAssociation count per type
ListOwnerIdsListOwnerIdsReqListOwnerIdsRespDistinct owner ids in a category
GetIntervalsIntervalsReqIntervalsRespDistinct forecast intervals
GetStaticSummaryEmptyReqStaticSummaryRespGrouped static-series summary
GetForecastSummaryEmptyReqForecastSummaryRespGrouped forecast summary
CheckStaticConsistencyConsistencyReqConsistencyRespPer-resolution static-grid check
ResolveForecastKeyResolveForecastKeyReqResolveForecastKeyRespAttributes + type → concrete key

TimeSeriesKey messages returned by GetTimeSeriesKeys, ListKeys, and ResolveForecastKey now carry the per-variant descriptive snapshot (initial_timestamp_rfc3339, length, horizon, count) in addition to the identity tuple, so RemoteClient reconstructs the full core TimeSeriesKey enum.

Common Messages

enum TimeSeriesType {
  SINGLE_TIME_SERIES               = 0;
  NON_SEQUENTIAL_TIME_SERIES       = 1;
  DETERMINISTIC                    = 2;
  DETERMINISTIC_SINGLE_TIME_SERIES = 3;
  PROBABILISTIC                    = 4;
  SCENARIOS                        = 5;
}

enum OwnerCategory { COMPONENT = 0; SUPPLEMENTAL_ATTRIBUTE = 1; }

message FeatureValue {
  oneof value {
    int64  int_value   = 1;
    double float_value = 2;
    bool   bool_value  = 3;
    string str_value   = 4;
  }
}

message Features { map<string, FeatureValue> entries = 1; }

message TimeSeriesKey {
  int64          owner_id         = 1;
  TimeSeriesType time_series_type = 2;
  string         name             = 3;
  string         resolution       = 4;   // ISO-8601 duration; empty = unset
  Features       features         = 5;
  OwnerCategory  owner_category   = 6;   // part of the owner identity / key
  string         interval         = 7;   // ISO-8601 duration; empty = unset
  // Descriptive snapshot (NOT part of the identity); variant implied by type.
  optional string initial_timestamp_rfc3339 = 8;
  optional uint64 length                     = 9;
  optional string horizon                    = 10;  // ISO-8601 duration
  optional uint64 count                      = 11;
}

message TimeSeriesMetadata {
  int64           owner_id                  = 1;
  string          owner_type                = 2;
  OwnerCategory   owner_category            = 3;
  TimeSeriesType  time_series_type          = 4;
  string          name                      = 5;
  bytes           data_hash                 = 6;   // 32 bytes
  // Temporal fields are `optional` so genuine values (e.g. length == 0) decode
  // correctly rather than colliding with a zero/empty sentinel.
  optional string initial_timestamp_rfc3339 = 7;
  optional string resolution                = 8;   // ISO-8601 duration
  optional uint64 length                    = 9;
  optional string horizon                   = 10;  // ISO-8601 duration
  optional string interval                  = 11;  // ISO-8601 duration
  optional uint64 count                     = 12;
  repeated string timestamps_rfc3339        = 13;
  Features        features                  = 14;
  optional string units                     = 16;
  int32           dtype                     = 17;  // Dtype code
  repeated uint64 element_shape             = 18;  // per-step trailing dims
  optional string ext                       = 19;  // opaque package-owned payload
  repeated double percentiles               = 20;  // Probabilistic only
}

Request / Response Messages

message ListReq {
  optional int64          owner_id         = 1;
  optional string         owner_type       = 2;
  optional TimeSeriesType time_series_type = 3;
  optional string         name             = 4;
  optional string         resolution       = 5;   // ISO-8601 duration
  Features                features         = 6;   // subset match
  optional OwnerCategory  owner_category   = 7;
  optional string         interval         = 8;   // ISO-8601 duration
}
message ListResp { repeated TimeSeriesMetadata metadata = 1; }

message GetReq {
  TimeSeriesKey   key           = 1;
  optional string start_rfc3339 = 2;   // optional time-axis slice; all-or-nothing with end
  optional string end_rfc3339   = 3;
}
message GetResp {
  string          initial_timestamp_rfc3339 = 1;
  string          resolution                = 2;   // ISO-8601 duration
  uint64          length                    = 3;
  repeated uint64 shape                     = 4;   // array dimensions (multi-dim supported)
  reserved 5;                                      // was: repeated double values
  TimeSeriesType  time_series_type          = 6;
  repeated string timestamps_rfc3339        = 7;   // set for NonSequentialTimeSeries
  int32           dtype                     = 8;   // Dtype code
  bytes           value_bytes               = 9;   // raw little-endian, row-major
  string          ext              = 10;
  // Forecast-specific fields (populated for Deterministic / Probabilistic / Scenarios).
  string          horizon                   = 11;  // ISO-8601 duration
  string          interval                  = 12;  // ISO-8601 duration
  uint64          count                     = 13;
  repeated double percentiles               = 14;  // Probabilistic only
  uint64          scenario_count            = 15;  // Scenarios only
}

message KeysReq  { int64 owner_id = 1; OwnerCategory owner_category = 2; }
message KeysResp { repeated TimeSeriesKey keys = 1; }

message ResolutionsReq  { optional TimeSeriesType time_series_type = 1; }
message ResolutionsResp { repeated string resolution = 1; }   // ISO-8601 durations

message CountsReq  {}
message CountsResp {
  int64 components_with_time_series = 1;
  int64 static_time_series          = 2;
  int64 forecasts                   = 3;
}

message ForecastParamsReq  {
  optional string resolution = 1;   // ISO-8601 duration filter
  optional string interval   = 2;   // ISO-8601 duration filter
}
message ForecastParamsResp {
  optional string horizon                   = 1;   // ISO-8601 duration
  optional string interval                  = 2;   // ISO-8601 duration
  optional uint64 count                     = 3;
  optional string resolution                = 4;   // ISO-8601 duration
  optional string initial_timestamp_rfc3339 = 5;
}

message HasReq  { TimeSeriesKey key = 1; }
message HasResp { bool present = 1; }

message VerifyReq  {}
message VerifyResp { repeated string errors = 1; }

GetReq Time Slice

start_rfc3339 and end_rfc3339 are all-or-nothing: supply both to request a time-axis slice, or neither to fetch the whole series. Setting exactly one is rejected with InvalidArgument ("start_rfc3339 and end_rfc3339 must be supplied together"). Each value must parse as RFC 3339; a malformed timestamp is also InvalidArgument.

Forecasts Over gRPC

The service is read-only, but its read surface covers dense forecasts. Forecast associations created through the Rust core or C ABI appear in ListTimeSeriesTimeSeriesMetadata carries horizon, interval (ISO-8601 durations), count, and (for Probabilistic) percentiles — and GetCounts includes them in forecasts.

GetTimeSeries returns forecast values too. For a Deterministic, DeterministicSingleTimeSeries (synthesized into Deterministic), Probabilistic, or Scenarios key it fills the GetResp array fields (value_bytes + dtype), the window parameters (horizon, interval, count), and the percentiles (Probabilistic) or scenario_count (Scenarios); the client reconstructs the matching type. Arrays are dtype-generic on the wire — value_bytes is the raw little-endian buffer and dtype names the element type (f64/f32/i64/i32/u64/bool), so non-f64 arrays survive the round trip without coercion. One caveat:

  • ext is not carried in GetResp. The opaque package-owned payload is returned by ListTimeSeries (on TimeSeriesMetadata) but left empty by GetTimeSeries, so a value fetched directly by key comes back without it.

Authentication

When the server is configured with method = "api_key", clients must send the key in the x-api-key request metadata (header). The server checks the supplied key against every configured key of the same length without early-exit, so a match is not leaked by timing; the comparison is not blinded against the supplied key's length, which is treated as non-secret. A missing or wrong key is rejected before the RPC runs. With method = "none" no metadata is required. See Server Configuration.

Rust Client

infrastore-server ships an async RemoteClient that mirrors the read methods and returns core types, mapping gRPC Status codes back onto the TimeSeriesError taxonomy:

gRPC CodeTimeSeriesError
NotFoundNotFound
AlreadyExistsDuplicateTimeSeries
InvalidArgumentInvalidParameter(message)
FailedPreconditionInvalidParameter(message)
DataLossIntegrityError(message)
anything elseConnectionError(code: message)

ConnectionError is only the fallback arm, so a remote NotFound or a rejected argument surfaces with the same variant a local Store would return.

#![allow(unused)]
fn main() {
use infrastore_core::OwnerCategory;
use infrastore_server::client::RemoteClient;

let client = RemoteClient::connect("http://127.0.0.1:50051".into()).await?;
let counts = client.get_counts().await?;
let keys = client.get_time_series_keys(42, OwnerCategory::Component).await?;
let data = client.get_time_series(&keys[0], None).await?;
}

RemoteClient methods: connect, from_channel, list_time_series, list_keys, get_metadata, get_time_series, bulk_read, get_time_series_keys, get_resolutions, get_intervals, get_counts, counts_by_type, time_series_counts_detailed, get_forecast_parameters, static_summary, forecast_summary, list_owner_ids, has_time_series, check_static_consistency, resolve_forecast_key, verify_integrity — the full read surface described above. See the gRPC Server guide for end-to-end usage and adding an API key to client requests.