- Replace form_urlencoded with RFC 3986 compliant URI encoding - Implement aws_uri_encode() matching AWS SigV4 spec exactly - Unreserved chars (A-Z,a-z,0-9,-,_,.,~) not encoded - All other chars percent-encoded with uppercase hex - Preserve slashes in paths, encode in query params - Normalize empty paths to '/' per AWS spec - Fix test expectations (body hash, HMAC values) - Add comprehensive SigV4 signature determinism test This fixes the canonicalization mismatch that caused signature validation failures in T047. Auth can now be enabled for production. Refs: T058.S1
469 lines
13 KiB
Protocol Buffer
469 lines
13 KiB
Protocol Buffer
syntax = "proto3";
|
|
|
|
package chainfire.v1;
|
|
|
|
// Key-Value service
|
|
service KV {
|
|
// Range gets the keys in the range from the key-value store
|
|
rpc Range(RangeRequest) returns (RangeResponse);
|
|
|
|
// Put puts the given key into the key-value store
|
|
rpc Put(PutRequest) returns (PutResponse);
|
|
|
|
// Delete deletes the given range from the key-value store
|
|
rpc Delete(DeleteRangeRequest) returns (DeleteRangeResponse);
|
|
|
|
// Txn processes multiple requests in a single transaction
|
|
rpc Txn(TxnRequest) returns (TxnResponse);
|
|
}
|
|
|
|
// Watch service
|
|
service Watch {
|
|
// Watch watches for events happening or that have happened
|
|
rpc Watch(stream WatchRequest) returns (stream WatchResponse);
|
|
}
|
|
|
|
// Cluster management service
|
|
service Cluster {
|
|
// MemberAdd adds a member into the cluster
|
|
rpc MemberAdd(MemberAddRequest) returns (MemberAddResponse);
|
|
|
|
// MemberRemove removes an existing member from the cluster
|
|
rpc MemberRemove(MemberRemoveRequest) returns (MemberRemoveResponse);
|
|
|
|
// MemberList lists all the members in the cluster
|
|
rpc MemberList(MemberListRequest) returns (MemberListResponse);
|
|
|
|
// Status gets the status of the cluster
|
|
rpc Status(StatusRequest) returns (StatusResponse);
|
|
|
|
// TransferSnapshot transfers a snapshot to a target node for pre-seeding
|
|
// This is used as a workaround for OpenRaft 0.9.x learner replication bug
|
|
rpc TransferSnapshot(TransferSnapshotRequest) returns (TransferSnapshotResponse);
|
|
|
|
// GetSnapshot returns the current snapshot from this node
|
|
rpc GetSnapshot(GetSnapshotRequest) returns (stream GetSnapshotResponse);
|
|
}
|
|
|
|
// Lease service for TTL-based key expiration
|
|
service Lease {
|
|
// LeaseGrant creates a new lease with a given TTL
|
|
rpc LeaseGrant(LeaseGrantRequest) returns (LeaseGrantResponse);
|
|
|
|
// LeaseRevoke revokes a lease, deleting all keys attached to it
|
|
rpc LeaseRevoke(LeaseRevokeRequest) returns (LeaseRevokeResponse);
|
|
|
|
// LeaseKeepAlive keeps a lease alive by refreshing its TTL
|
|
rpc LeaseKeepAlive(stream LeaseKeepAliveRequest) returns (stream LeaseKeepAliveResponse);
|
|
|
|
// LeaseTimeToLive retrieves lease information
|
|
rpc LeaseTimeToLive(LeaseTimeToLiveRequest) returns (LeaseTimeToLiveResponse);
|
|
|
|
// LeaseLeases lists all existing leases
|
|
rpc LeaseLeases(LeaseLeasesRequest) returns (LeaseLeasesResponse);
|
|
}
|
|
|
|
// Response header included in all responses
|
|
message ResponseHeader {
|
|
// cluster_id is the ID of the cluster
|
|
uint64 cluster_id = 1;
|
|
// member_id is the ID of the responding member
|
|
uint64 member_id = 2;
|
|
// revision is the key-value store revision
|
|
int64 revision = 3;
|
|
// raft_term is the current Raft term
|
|
uint64 raft_term = 4;
|
|
}
|
|
|
|
// Key-value pair
|
|
message KeyValue {
|
|
// key is the key in bytes
|
|
bytes key = 1;
|
|
// create_revision is the revision of last creation
|
|
int64 create_revision = 2;
|
|
// mod_revision is the revision of last modification
|
|
int64 mod_revision = 3;
|
|
// version is the version of the key
|
|
int64 version = 4;
|
|
// value is the value held by the key
|
|
bytes value = 5;
|
|
// lease is the ID of the lease attached to the key
|
|
int64 lease = 6;
|
|
}
|
|
|
|
// ========== Range ==========
|
|
|
|
message RangeRequest {
|
|
// key is the first key for the range
|
|
bytes key = 1;
|
|
// range_end is the upper bound on the requested range
|
|
bytes range_end = 2;
|
|
// limit is a limit on the number of keys returned
|
|
int64 limit = 3;
|
|
// revision is the point-in-time of the store to use
|
|
int64 revision = 4;
|
|
// keys_only when set returns only the keys and not the values
|
|
bool keys_only = 5;
|
|
// count_only when set returns only the count of the keys
|
|
bool count_only = 6;
|
|
// serializable sets the range request to use serializable (local) reads.
|
|
// When true, reads from local state (faster, but may be stale).
|
|
// When false (default), uses linearizable reads through Raft (consistent).
|
|
bool serializable = 7;
|
|
}
|
|
|
|
message RangeResponse {
|
|
ResponseHeader header = 1;
|
|
// kvs is the list of key-value pairs matched by the range request
|
|
repeated KeyValue kvs = 2;
|
|
// more indicates if there are more keys to return
|
|
bool more = 3;
|
|
// count is set to the number of keys within the range
|
|
int64 count = 4;
|
|
}
|
|
|
|
// ========== Put ==========
|
|
|
|
message PutRequest {
|
|
// key is the key to put
|
|
bytes key = 1;
|
|
// value is the value to put
|
|
bytes value = 2;
|
|
// lease is the lease ID to attach to the key
|
|
int64 lease = 3;
|
|
// prev_kv when set returns the previous key-value pair
|
|
bool prev_kv = 4;
|
|
}
|
|
|
|
message PutResponse {
|
|
ResponseHeader header = 1;
|
|
// prev_kv is the key-value pair before the put
|
|
KeyValue prev_kv = 2;
|
|
}
|
|
|
|
// ========== Delete ==========
|
|
|
|
message DeleteRangeRequest {
|
|
// key is the first key to delete
|
|
bytes key = 1;
|
|
// range_end is the key following the last key to delete
|
|
bytes range_end = 2;
|
|
// prev_kv when set returns deleted key-value pairs
|
|
bool prev_kv = 3;
|
|
}
|
|
|
|
message DeleteRangeResponse {
|
|
ResponseHeader header = 1;
|
|
// deleted is the number of keys deleted
|
|
int64 deleted = 2;
|
|
// prev_kvs holds the deleted key-value pairs
|
|
repeated KeyValue prev_kvs = 3;
|
|
}
|
|
|
|
// ========== Transaction ==========
|
|
|
|
message TxnRequest {
|
|
// compare is a list of predicates
|
|
repeated Compare compare = 1;
|
|
// success is a list of operations to apply if all comparisons succeed
|
|
repeated RequestOp success = 2;
|
|
// failure is a list of operations to apply if any comparison fails
|
|
repeated RequestOp failure = 3;
|
|
}
|
|
|
|
message TxnResponse {
|
|
ResponseHeader header = 1;
|
|
// succeeded is set to true if all comparisons evaluated to true
|
|
bool succeeded = 2;
|
|
// responses is a list of responses corresponding to the results
|
|
repeated ResponseOp responses = 3;
|
|
}
|
|
|
|
message Compare {
|
|
enum CompareResult {
|
|
EQUAL = 0;
|
|
GREATER = 1;
|
|
LESS = 2;
|
|
NOT_EQUAL = 3;
|
|
}
|
|
enum CompareTarget {
|
|
VERSION = 0;
|
|
CREATE = 1;
|
|
MOD = 2;
|
|
VALUE = 3;
|
|
}
|
|
CompareResult result = 1;
|
|
CompareTarget target = 2;
|
|
bytes key = 3;
|
|
oneof target_union {
|
|
int64 version = 4;
|
|
int64 create_revision = 5;
|
|
int64 mod_revision = 6;
|
|
bytes value = 7;
|
|
}
|
|
}
|
|
|
|
message RequestOp {
|
|
oneof request {
|
|
RangeRequest request_range = 1;
|
|
PutRequest request_put = 2;
|
|
DeleteRangeRequest request_delete_range = 3;
|
|
}
|
|
}
|
|
|
|
message ResponseOp {
|
|
oneof response {
|
|
RangeResponse response_range = 1;
|
|
PutResponse response_put = 2;
|
|
DeleteRangeResponse response_delete_range = 3;
|
|
}
|
|
}
|
|
|
|
// ========== Watch ==========
|
|
|
|
message WatchRequest {
|
|
oneof request_union {
|
|
WatchCreateRequest create_request = 1;
|
|
WatchCancelRequest cancel_request = 2;
|
|
WatchProgressRequest progress_request = 3;
|
|
}
|
|
}
|
|
|
|
message WatchCreateRequest {
|
|
// key is the key to watch
|
|
bytes key = 1;
|
|
// range_end is the end of the range to watch
|
|
bytes range_end = 2;
|
|
// start_revision is an optional revision to start watching from
|
|
int64 start_revision = 3;
|
|
// progress_notify is set to true to enable progress notifications
|
|
bool progress_notify = 4;
|
|
// prev_kv when set includes previous key-value in events
|
|
bool prev_kv = 5;
|
|
// watch_id is the user-provided watch ID (0 for server-assigned)
|
|
int64 watch_id = 6;
|
|
}
|
|
|
|
message WatchCancelRequest {
|
|
// watch_id is the watch ID to cancel
|
|
int64 watch_id = 1;
|
|
}
|
|
|
|
message WatchProgressRequest {}
|
|
|
|
message WatchResponse {
|
|
ResponseHeader header = 1;
|
|
// watch_id is the watch ID for this response
|
|
int64 watch_id = 2;
|
|
// created is set to true if this response is for a create request
|
|
bool created = 3;
|
|
// canceled is set to true if the watch was canceled
|
|
bool canceled = 4;
|
|
// compact_revision is the minimum revision the watcher may receive
|
|
int64 compact_revision = 5;
|
|
// cancel_reason indicates the reason for cancellation
|
|
string cancel_reason = 6;
|
|
// events is the list of events in this response
|
|
repeated Event events = 11;
|
|
}
|
|
|
|
message Event {
|
|
enum EventType {
|
|
PUT = 0;
|
|
DELETE = 1;
|
|
}
|
|
// type is the kind of event
|
|
EventType type = 1;
|
|
// kv is the KeyValue affected by the event
|
|
KeyValue kv = 2;
|
|
// prev_kv is the KeyValue prior to the event
|
|
KeyValue prev_kv = 3;
|
|
}
|
|
|
|
// ========== Cluster Management ==========
|
|
|
|
message Member {
|
|
// ID is the member ID
|
|
uint64 id = 1;
|
|
// name is the human-readable name
|
|
string name = 2;
|
|
// peer_urls are URLs for Raft communication
|
|
repeated string peer_urls = 3;
|
|
// client_urls are URLs for client communication
|
|
repeated string client_urls = 4;
|
|
// is_learner indicates if member is a learner
|
|
bool is_learner = 5;
|
|
}
|
|
|
|
message MemberAddRequest {
|
|
// node_id is the joining node's actual ID
|
|
uint64 node_id = 1;
|
|
// peer_urls are the URLs to reach the new member
|
|
repeated string peer_urls = 2;
|
|
// is_learner indicates if the member is a learner
|
|
bool is_learner = 3;
|
|
}
|
|
|
|
message MemberAddResponse {
|
|
ResponseHeader header = 1;
|
|
// member is the member information for the added member
|
|
Member member = 2;
|
|
// members is the list of all members after adding
|
|
repeated Member members = 3;
|
|
}
|
|
|
|
message MemberRemoveRequest {
|
|
// ID is the member ID to remove
|
|
uint64 id = 1;
|
|
}
|
|
|
|
message MemberRemoveResponse {
|
|
ResponseHeader header = 1;
|
|
// members is the list of all members after removing
|
|
repeated Member members = 2;
|
|
}
|
|
|
|
message MemberListRequest {}
|
|
|
|
message MemberListResponse {
|
|
ResponseHeader header = 1;
|
|
// members is the list of all members
|
|
repeated Member members = 2;
|
|
}
|
|
|
|
message StatusRequest {}
|
|
|
|
message StatusResponse {
|
|
ResponseHeader header = 1;
|
|
// version is the version of the server
|
|
string version = 2;
|
|
// db_size is the size of the database
|
|
int64 db_size = 3;
|
|
// leader is the member ID of the current leader
|
|
uint64 leader = 4;
|
|
// raft_index is the current Raft committed index
|
|
uint64 raft_index = 5;
|
|
// raft_term is the current Raft term
|
|
uint64 raft_term = 6;
|
|
// raft_applied_index is the current Raft applied index
|
|
uint64 raft_applied_index = 7;
|
|
}
|
|
|
|
// ========== Lease ==========
|
|
|
|
message LeaseGrantRequest {
|
|
// TTL is the advisory time-to-live in seconds
|
|
int64 ttl = 1;
|
|
// ID is the requested lease ID. If 0, the server will choose an ID.
|
|
int64 id = 2;
|
|
}
|
|
|
|
message LeaseGrantResponse {
|
|
ResponseHeader header = 1;
|
|
// ID is the lease ID for the granted lease
|
|
int64 id = 2;
|
|
// TTL is the actual TTL granted by the server
|
|
int64 ttl = 3;
|
|
// error is any error that occurred
|
|
string error = 4;
|
|
}
|
|
|
|
message LeaseRevokeRequest {
|
|
// ID is the lease ID to revoke
|
|
int64 id = 1;
|
|
}
|
|
|
|
message LeaseRevokeResponse {
|
|
ResponseHeader header = 1;
|
|
}
|
|
|
|
message LeaseKeepAliveRequest {
|
|
// ID is the lease ID to keep alive
|
|
int64 id = 1;
|
|
}
|
|
|
|
message LeaseKeepAliveResponse {
|
|
ResponseHeader header = 1;
|
|
// ID is the lease ID from the keep-alive request
|
|
int64 id = 2;
|
|
// TTL is the new TTL for the lease
|
|
int64 ttl = 3;
|
|
}
|
|
|
|
message LeaseTimeToLiveRequest {
|
|
// ID is the lease ID to query
|
|
int64 id = 1;
|
|
// keys is true to query all keys attached to this lease
|
|
bool keys = 2;
|
|
}
|
|
|
|
message LeaseTimeToLiveResponse {
|
|
ResponseHeader header = 1;
|
|
// ID is the lease ID
|
|
int64 id = 2;
|
|
// TTL is the remaining TTL in seconds; -1 if lease doesn't exist
|
|
int64 ttl = 3;
|
|
// grantedTTL is the initial TTL granted
|
|
int64 granted_ttl = 4;
|
|
// keys is the list of keys attached to this lease
|
|
repeated bytes keys = 5;
|
|
}
|
|
|
|
message LeaseLeasesRequest {}
|
|
|
|
message LeaseLeasesResponse {
|
|
ResponseHeader header = 1;
|
|
// leases is the list of all leases
|
|
repeated LeaseStatus leases = 2;
|
|
}
|
|
|
|
message LeaseStatus {
|
|
// ID is the lease ID
|
|
int64 id = 1;
|
|
}
|
|
|
|
// ========== Snapshot Transfer (T041 Option C workaround) ==========
|
|
|
|
// Snapshot metadata
|
|
message SnapshotMeta {
|
|
// last_log_index is the last log index included in the snapshot
|
|
uint64 last_log_index = 1;
|
|
// last_log_term is the term of the last log entry included
|
|
uint64 last_log_term = 2;
|
|
// membership is the cluster membership at snapshot time
|
|
repeated uint64 membership = 3;
|
|
// size is the size of snapshot data in bytes
|
|
uint64 size = 4;
|
|
}
|
|
|
|
// Request to transfer snapshot to a target node
|
|
message TransferSnapshotRequest {
|
|
// target_node_id is the ID of the node to receive the snapshot
|
|
uint64 target_node_id = 1;
|
|
// target_addr is the gRPC address of the target node
|
|
string target_addr = 2;
|
|
}
|
|
|
|
// Response from snapshot transfer
|
|
message TransferSnapshotResponse {
|
|
ResponseHeader header = 1;
|
|
// success indicates if the transfer completed successfully
|
|
bool success = 2;
|
|
// error is the error message if transfer failed
|
|
string error = 3;
|
|
// meta is the metadata of the transferred snapshot
|
|
SnapshotMeta meta = 4;
|
|
}
|
|
|
|
// Request to get snapshot from this node
|
|
message GetSnapshotRequest {}
|
|
|
|
// Streaming response containing snapshot chunks
|
|
message GetSnapshotResponse {
|
|
// meta is the snapshot metadata (only in first chunk)
|
|
SnapshotMeta meta = 1;
|
|
// chunk is the snapshot data chunk
|
|
bytes chunk = 2;
|
|
// done indicates if this is the last chunk
|
|
bool done = 3;
|
|
}
|