Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save abourget/705c43e801d354a0d1dc8d53dd90e9ca to your computer and use it in GitHub Desktop.
Save abourget/705c43e801d354a0d1dc8d53dd90e9ca to your computer and use it in GitHub Desktop.

Problem:

  • We want to keep the experiments for payments/billing/usage report/authentication coherent and unified
  • We also want to have multiple distributed gateways of the network.
  • These are the tensions between a centralizing vs decentralizing different components.

Solution:

  • Provide an API that allows someone to emerge as a great UI for payments/billing/usage report/authentication, yet distributes the provisioning of the service to various gateways operated by multiple organisations.
  • By defining a protocol, there also exists competition at that UI level, but gateway operators/providers of service would have an established way of communicating with that UI, in a standardized way, and move to use the best front-end (or use multiple).
  • May the best win!

Notes

Some signed JWTs from thegraph.com could be honored at StreamingFast, and include an endpoint to callback to post usage information to a Usage API.

With that callback, the auth originator, which is also expected to bill, would pay back at some agreed upon rates to the SF service provider, what is billed to the consumer.

The JWT would need to contain a sort of pre-approved rate or something?

Two example players:

  • Issuer (running the Auth and Billing Endpoint)
  • Provider (Provider of data services, which receives the signed JWT and calls back ABP endpoints)

The issuer signs a JWT for user 123:

{
  "sub": "123", // user ID at issuer `streamingfast`
  "iss": "abp.streamingfast.io", // issuer AND endpoint for contacting, implied port :443, and https, and gRPC
  "id": "uuuid-4-123123123123",
  "iat": 999999999999,
}

Provider A receives a request with that JWT, checks signature. Provider A needs to be configured to honor the Issuer's keys.

The revocation endpoint could return the amount of time before the next poll. It could also support a streaming version, where revocations are "pushed" through.

Contacting the ABP's endpoint could also be authenticated, you need to chat and obtain some auth token to discuss with them. You wouldn't peer permissionlessly, but you could peer with anyone in the network that respects the protocol.

Model

// for Auth and Billing Protocol
package sf.abp.v1

// The only thing pointed to by the "abp" field in the JWT. A service discovery endpoint, so you can split the services.
service Discover {
  rpc Services(Empty) returns (ServicesResp);
}

message ServicesResp {
  string usage_endpoint = 1; // points to the "Usage" service endpoint
  string auth_endpoint = 2; // .. for "Auth"
  string revocation_endpoint = 3; // .. for "Revocation"
}

service Usage {
  rpc ReportStream(stream ReportUsage) returns (ReportResp);
  rpc Report(ReportUsage) returns (ReportResp);
}

message ReportUsage {
  repeated Usage usages = 1;
  
  message Usage {
    string user_id = 1;
    string jwt_id = 5;
    string service_endpoint = 2;
    string metric_type = 3;
    string network = 4;
    uint64 start_timestamp = 6;
    uint64 end_timestamp = 7;
    
    repeated Metric metrics = 8;  
    message Metric {
      string type = 1;
      uint64 value = 2;
    }
  }
}

message ReportResp {
  bool revoked = 1;
  string revocation_reason = 2;
}

service Auth {
  rpc Authenticate(AuthReq) returns (AuthResp);
}

// AuthReq could be done through the portal, to avoid a programmatic
// resolution
message AuthReq { // similar to a request to our jwt issuer.
  string api_key = 1;
  optional string origin = 2;
  optional string ip_addr = 3; 
  uint64 lifetime_seconds = 4; // usually long-lived
}

message AuthResp {
  bool success = 2;
  string jwt = 1;
  uint64 expiration = 3;
  string user_id = 4;
  string jwt_id = 5;
  //bytes key_authority = 6; // Who signed this? What's the public key? For us to validate whether we will honor this partner. Say this is E&N's, are we configured to honor those keys?
}

// Maybe not needed if the Usage callback returns some
// potential revocation information.
service Revocation {
  rpc Check(CheckReq) returns (CheckResp);
  rpc Announce(AnnounceReq) returns (streams RevocationAnnouncement);
}

message CheckReq {
  string jwt_id = 1;
  string user_id = 2;
}
message CheckResp {
  bool all_good = 1;
  string revocation_reason = 2;
}
message AnnounceReq {}
message RevocationAnnouncement {
  optional string revoked_jwt_id = 1;
  optional string revoked_user_id = 2;
}

Communication to the ABP can be secured with a pre-shared key, to start with. The services configs could hold a simple mapping like:

--accept psk_123123123_234234234_graphops:graphops --accept psk_234234234_345345345_pinax:pinax --accept psk_345345345_456456456_en:edgeandnode

or from a file. The name after the key is used to write to storage who is the source of the usage, to be able to do isolated reporting, and bill the right party.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment