Skip to content

Connecting over gRPC

Jennah publishes the API on two endpoints:

Endpoint Protocol Use Case
jennah.alphaus.cloud HTTP/JSON Console, browser sign-in flows, and HTTP clients
jennah-grpc.alphaus.cloud gRPC SDK and automated integrations, authenticated with an API key or a signed-in session

Both endpoints route to the same underlying service and share the same authentication and authorization engine. A credential yields identical behavior and access rights regardless of which endpoint is used.

Connect

TLS terminates at the load balancer on port 443. Standard TLS configuration is sufficient; custom certificates or plaintext ports are not required.

The Go SDK dials this endpoint by default and attaches the credential to every call:

import jennah "github.com/alphauslabs/jennah-sdk-go"

jc, err := jennah.NewClient(jennah.Config{
    APIKey: os.Getenv("JENNAH_API_KEY"), // optional, see Credentials below
})
if err != nil {
    return err
}
defer jc.Close()

// Optional: confirm the endpoint is reachable and serving.
if err := jc.Ping(ctx); err != nil {
    return err
}

res, err := jc.Agent("agent-abc").Vectors.Search(ctx, &jennah.SemanticQuery{
    Embedding: embedding,
    Limit:     5,
})
import (
    agentpb "github.com/alphauslabs/jennah-sdk-go/jennah/agent/v1"
    "google.golang.org/grpc"
    "google.golang.org/grpc/credentials"
)

conn, err := grpc.NewClient("jennah-grpc.alphaus.cloud:443",
    grpc.WithTransportCredentials(credentials.NewTLS(nil)))
if err != nil {
    return err
}
defer conn.Close()

client := agentpb.NewMemoryServiceClient(conn)
grpcurl -H "authorization: Bearer $JENNAH_API_KEY" \
  jennah-grpc.alphaus.cloud:443 \
  jennahapi.platform.v1.PlatformService/ListLocations

Authenticate

Pass the credential in the authorization metadata header, using the same format as HTTP requests:

authorization: Bearer jennah_sk_...

The endpoint accepts both API keys (jennah_sk_ prefix) and access tokens generated from sign-in flows. The server identifies the credential type by its prefix and resolves the identity accordingly. Scopes and selectors apply identically across gRPC and HTTP requests.

Attach the credential per call, not per connection

gRPC metadata is sent with each RPC. Use a PerRPCCredentials implementation or set metadata per request rather than expecting connection dialing to persist credentials.

Credentials in the Go SDK

Config.APIKey is optional. Left empty, the SDK resolves a credential itself, taking the first source that answers and consulting no further:

  1. Config.Credentials, a source the program supplies (a secret manager, a test double, or a session it renews on its own terms).
  2. Config.APIKey.
  3. the JENNAH_API_KEY environment variable.
  4. the session stored by jnh login.

So a developer who has signed in with the CLI needs no configuration at all:

jc, err := jennah.NewClient(jennah.Config{}) // the session from `jnh login`

while a deployed service that sets a key is unaffected: a later source is never read, so a container with no session file cannot fail a caller who supplied one. When no source yields a credential, construction fails with an error naming how to obtain one, rather than surfacing as a rejection on the first call.

The stored session lives in the per-user config directory, at ~/.config/jennah/credentials on Linux (it honors XDG_CONFIG_HOME), readable only by its owner. It is the same file the CLI reads and writes, so jnh login in one shell authenticates an SDK program in another.

The endpoint recorded in a stored session is ignored

A session written by the CLI names the front door it was obtained through, which is the HTTP gateway, and that hostname cannot answer a gRPC call. The SDK dials Config.Endpoint, or the gRPC endpoint above when that is empty.

Client.Credential reports which credential was resolved and where it came from, never its value, so it is safe to log:

// Prints "session from stored session", or "api key from $JENNAH_API_KEY".
log.Printf("authenticated with %s", jc.Credential())

Renewal

A resolved session renews itself. When the platform rejects the access token, the SDK refreshes it and reissues the call exactly once; a second rejection is returned to the caller. A long-running program keeps working across an expiry with nothing to schedule or refresh ahead of time.

Refreshing rotates the refresh token, so a renewed session is written back to the shared credentials file before it is relied on. A renewal kept in memory would leave every other reader of that file, the CLI included, holding a token the platform will never accept again. Concurrent calls renew once between them.

An API key is never renewed. It has no refresh token, so a rejection means the key itself was refused: the error is credentials.ErrKeyRefused, which still reads as jennah.IsUnauthenticated.

Endpoint coverage and limitations

All services are available on this endpoint. The gRPC endpoint exposes all registered server operations, subject to standard credential authorization rules.

Enterprise administration tasks (such as inviting members, changing roles, minting keys, or transferring ownership) require a signed-in user access token. API key scopes cannot grant administrative permissions, so requests using an API key will be rejected over gRPC just as they are over HTTP.

Browsers do not support raw gRPC. Web frontends and web applications must use the HTTP endpoint.

The jnh CLI uses HTTP. It is built using the published protobuf messages available to external integrators, ensuring CLI operations mirror standard public HTTP integration pathways.

Sign-in flows require HTTP. Although login and device-code RPCs exist in the protobuf definitions, authentication flows rely on browser redirects and cookies managed by the HTTP endpoint. Use the HTTP endpoint to authenticate, then use gRPC with the resulting credential.