Skip to content

ConnectRPC API

Elara's management API is a set of 14 ConnectRPC services served from the HTTP/2 server on port 8080, alongside the embedded Web UI. The Web UI is itself a client of this API — anything the UI can do, an external client can do.

Transport

ConnectRPC handlers speak three protocols on the same endpoint, and the server picks one per request from the Content-Type:

Protocol Content-Type Notes
gRPC application/grpc Wire-compatible with any gRPC client/codegen
gRPC-Web application/grpc-web For browsers / proxies that cannot do trailers
Connect application/json, application/proto Connect's own protocol — unary calls are plain HTTP POST, so curl works

Procedure URLs follow the standard gRPC path shape, /<proto.package>.<Service>/<Method>, e.g.:

curl -X POST https://elara.example.com/elara.namespace.v1.NamespaceService/ListNamespaces \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer <session-id>' \
  -d '{}'

Streaming RPCs (WatchConfigs, WatchClients, WatchClient) are server-streaming and require HTTP/2 (gRPC / Connect streaming); they are not usable over plain unary JSON POST.

This is not the etcd API

Port 8080 is the management/UI surface. Config consumers use the etcd-compatible gRPC API on port 2379 with a service token — see Client Auth (etcd). The two are separate ports, separate protocols, and separate credentials.

Authentication

Every service except AuthService is mounted behind the auth interceptor (privateOpts in internal/di/service/handler.go) and requires a session — either the elara_session cookie or Authorization: Bearer <session-id> (the Bearer header wins if both are present).

Three services are also conditionally mounted: if their feature flag is off, the routes do not exist at all and calls return HTTP 404, not Unauthenticated.

Service Auth Mounted when
AuthService Public — no session required always
CapabilitiesService Session required always
ProfileService Session required always
ConfigService, SchemaService Session required always
NamespaceService Session required always
ClientsService, DashboardService, FilterService Session required always
TransferService, WebhookService Session required always
UserService, GroupService Session required ui.auth.enabled=true
TokenService Session required client.auth.enabled=true

Forced password change blocks almost everything

While the session's user has password_change_required set (bootstrap basic-auth admin, or after ResetUserPassword), the interceptor rejects every procedure except exactly three: ProfileService/ChangePassword, ProfileService/Me, and ProfileService/Logout.

In passthrough mode (ui.auth.enabled=false) the interceptor runs with skipPermissions=true and rewrites every request — authenticated or not — as a synthetic superadmin. Do not expose such an instance.

Beyond authentication, most write RPCs are additionally authorized against the groups-only RBAC model, with anti-escalation checks (you cannot grant what you do not hold). GroupService and UserService document their exact rules in the .proto comments.


AuthService

Login and auth-mode discovery. The only public service — it is what an unauthenticated client calls to obtain a session.

RPC Description
GetAuthInfo Returns the server's configured AuthType (none / basic-auth / oidc) so a client knows which login flow to drive.
OIDCLogin Starts the OIDC flow; returns the IdP redirect_url to send the browser to.
OIDCCallback Exchanges the IdP code + state for a session; sets the elara_session cookie.
BasicLogin Password login with email + password; sets the session cookie and returns password_change_required.

There is no Login-side logout — logout lives on ProfileService, which is authenticated by definition.

ProfileService

The current session's own identity and credentials.

RPC Description
Me Returns the caller's email, name, picture, password_change_required, and the full flattened list of permission assignments the UI uses for client-side gating.
ChangePassword Sets a new password. current_password is required for a voluntary change but may be omitted when password_change_required is set. Basic-auth only — returns InvalidArgument under OIDC/passthrough.
Logout Revokes the current session and clears the cookie.

CapabilitiesService

Server feature flags, so a client can hide UI/skip calls for services that are not mounted.

RPC Description
GetCapabilities Returns etcd_token_auth_enabled (is TokenService mounted?), user_management_enabled (are UserService/GroupService mounted?), and demo_mode (instance seeded with sample data).

ConfigService

CRUD, history, and locking for config entries. This is the primary data-plane service of the management API.

RPC Description
CreateConfig Creates a config at path in namespace with content, format, and metadata.
GetConfig Reads the current revision of one config by namespace + path.
UpdateConfig Writes new content. Pass version for optimistic concurrency.
DeleteConfig Deletes a config.
ListConfigs Directory-style listing: returns the entries under a folder path (""// = root), paginated and sortable, with optional name-substring query.
GetConfigHistory Returns the newest-first revision history for a path (limit defaults to 20).
GetConfigAtRevision Returns the content of one specific historical revision.
SearchConfigs Full search across configs by query, optionally scoped to a namespace; paginated.
CopyConfig Copies a config from a source namespace+path to a destination pair (cross-namespace allowed).
ValidateConfig Validates candidate content against the schema that would apply at namespace+path, without writing.
WatchConfigs Server stream. Emits change events for a path_prefix within a namespace (CREATED, UPDATED, DELETED, LOCKED, UNLOCKED, NAMESPACE_LOCKED, NAMESPACE_UNLOCKED).
GetConfigDiff Returns from_content, to_content, and a rendered textual diff between two revisions.
LockConfig Marks a single config read-only.
UnlockConfig Clears the lock.

SchemaService

JSON Schema attachments that validate config content on write.

RPC Description
AttachSchema Attaches a json_schema to a path_pattern within a namespace (creates or replaces).
DetachSchema Removes the attachment for an exact path_pattern.
GetSchema Fetches the attachment registered for an exact path_pattern.
GetEffectiveSchema Resolves which attachment actually applies to a concrete path — use this, not GetSchema, to answer "what validates this config?".
ListSchemas All attachments in a namespace.

NamespaceService

Namespaces are the top-level tenancy and RBAC domain unit.

RPC Description
CreateNamespace Creates a namespace (name is validated against the namespace name pattern).
GetNamespace Fetches one namespace by name.
UpdateNamespace Updates the description only — namespace names are immutable.
ListNamespaces Paginated, sortable listing with an optional name-substring query.
DeleteNamespace Deletes a namespace.
LockNamespace Marks the whole namespace read-only; emits a NAMESPACE_LOCKED watch event.
UnlockNamespace Clears the namespace lock.

UserService

User records and their group memberships. Mounted only when ui.auth.enabled=true.

RPC Description
ListUsers Paginated listing. With User:Read * returns everyone; otherwise the result is derived through Group:Read — only users in groups the caller can read (unassigned users are invisible).
GetUser One user by UUID. visible_group_ids is independently filtered by the caller's Group:Read scope.
CreateUser Creates a user, optionally joining initial_group_ids atomically. A password is required in basic-auth mode and must be empty under OIDC. Rejected with ErrFeatureNotAvailable when the auth type is none — passthrough has no identities to create.
UpdateUserGroups Explicit membership delta (add_group_ids / remove_group_ids). Adds/removes that are no-ops are accepted; the same id in both lists is InvalidArgument. Supports expected_version optimistic locking (FailedPrecondition on mismatch).
ResetUserPassword Admin reset — sets a new password and password_change_required. Basic-auth mode only.
DeactivateUser Deactivates the user and revokes all their sessions in one transaction. Does not revoke tokens they issued.
ReactivateUser Reactivates a deactivated user. Revoked sessions are not restored — they must log in again.
DeleteUser Deletes the user and all their memberships. Basic-auth mode only. System users (is_system) cannot be deleted or deactivated.

GroupService

Groups are the only way permissions reach a user. Mounted only when ui.auth.enabled=true.

RPC Description
CreateGroup Creates a group, optionally with initial_permissions, initial_member_emails, and initial_manager_group_names (groups granted Group:Write over the new group). The caller must hold every permission being granted.
GetGroup One group by name. The full permission set is returned; visible_members is filtered through the caller's derived User:Read scope.
UpdateGroup Metadata only — display_name and description. Members and permissions have their own RPCs by design.
UpdateGroupMembers Explicit membership delta by email. Anti-escalation: each added member inherits the group's whole permission set, so the caller must hold all of it. expected_members_version gives optimistic locking.
UpdateGroupPermissions Explicit permission delta. Checked both per-delta (caller holds each added permission) and as a cascade (caller holds everything the group will hold, if it has members). expected_permissions_version gives optimistic locking.
DeleteGroup Deletes the group and all of its membership and permission rules in one Casbin write. System groups return FailedPrecondition.
ListGroups With Group:Read * returns every group; otherwise only groups the caller can read individually. An empty page is a valid result, not an error.

TokenService

Service credentials for the etcd-compatible gRPC API on :2379. Mounted only when client.auth.enabled=true.

RPC Description
CreateToken Issues a token with a name, an explicit namespaces list (* = all), a permission (reader/writer — never admin), and an optional expires_at. The raw_token is returned exactly once here; only its hash is stored.
ListTokens Paginated, sortable, filterable listing of token metadata (never the secret).
GetToken One token's metadata by id.
RevokeToken Revokes a token by id. This is the only way a token dies early — issuer lifecycle events never cascade to tokens.

Note

A token's permissions come from its own Role and Namespaces, not from the user who issued it. See Sessions & Tokens.


ClientsService

Observability over the etcd-API clients currently connected to :2379. Connection history is persisted; per-RPC event logs are in-memory and bounded.

RPC Description
ListActiveClients All currently-connected clients.
GetClient One client (active or recently disconnected) plus its recent in-memory event log. recent_events is empty for disconnected clients — those events are not persisted.
ListHistoricalConnections Past connections, newest first, limit-bounded (0 → server default).
ListClientSessions Past connections of the same logical client, matched on client_name + k8s_namespace, newest first; the current_id session is excluded if given.
WatchClients Server stream. Periodic full SNAPSHOT updates plus immediate CONNECTED/DISCONNECTED events. Feeds a live list view; ends when the caller closes the stream.
WatchClient Server stream for a single client: snapshot updates plus REQUEST_RECORDED per-RPC events for a tailing activity log. Ends when that client disconnects.

DashboardService

Aggregate counters and a recent-changes feed for the UI home screen.

RPC Description
GetStats namespace_count, config_count, active_client_count, and the current global_revision.
ListActivity Recent config change entries (revision, event type, path, namespace, version, timestamp). limit defaults to 50.

FilterService

Read-only lookup data for building permission-aware pickers and forms. Every listing RPC accepts an actions filter so the UI can ask "which namespaces can I write to?" rather than post-filtering client-side.

RPC Description
GetNamespaces Namespace picker items, narrowed to those on which the caller holds the requested actions.
GetGroups Group picker items, same actions narrowing.
GetUsers User picker items, same actions narrowing.
GetPermissionCatalog The static catalog for permission-assignment forms: per PermissionObject, which PermissionActions are meaningful and what domain the assignment must carry — GLOBAL (domain must be *), NAMESPACE (a namespace name or *), or GROUP (group:<name> or *). The server validates assignments against the same catalog.

TransferService

Bulk import/export of configs as a bundle (JSON or YAML, optionally zipped).

RPC Description
ExportNamespace Exports one namespace. Returns raw data plus content_type and a suggested filename.
ExportAll Exports every namespace. zip_layout selects a single BUNDLE file or one file PER_NAMESPACE.
ImportNamespace Imports a bundle. on_conflict is SKIP / OVERWRITE / FAIL; dry_run validates without writing; namespace overrides the bundle's namespace (and then rejects an all-namespaces bundle). Returns created / updated / skipped / failed counts plus per-entry errors.

WebhookService

Outbound HTTP notifications on config changes.

RPC Description
CreateWebhook Registers a url with a namespace_filter, path_prefix, an events list (CREATED / UPDATED / DELETED), an HMAC secret, and an enabled flag.
GetWebhook One webhook by id.
UpdateWebhook Updates any of the above fields by id.
DeleteWebhook Removes a webhook.
ListWebhooks All registered webhooks.
GetDeliveryHistory Recent delivery attempts for one webhook — status, timing, and failures, for debugging endpoints that are not receiving events.

Generating a client

Protobuf sources live in proto/elara/<area>/v1/. make generate runs buf generate and emits, per buf.gen.yaml:

Target Output Plugin
Go messages internal/proto/elara/<area>/v1/*.pb.go protocolbuffers/go
Go Connect clients/handlers internal/proto/elara/<area>/v1/<area>v1connect/*.go connectrpc/go
TypeScript messages web/src/gen/elara/<area>/v1/*_pb.ts bufbuild/es
TypeScript TanStack Query hooks web/src/gen/elara/<area>/v1/*-<Service>Query.ts connectrpc/query-es

Both generated trees are committed, so a Go client can import the *v1connect packages directly. For clients outside this repo, point your own buf generate (or protoc) at proto/ — the service definitions are the contract, and any gRPC-capable language works via the gRPC protocol.

This page is hand-maintained

buf.gen.yaml has no protoc-gen-doc plugin — nothing regenerates this page. When you add, rename, or remove an RPC, update the relevant table here in the same change. The .proto files remain the source of truth for exact field names, validation rules, and authorization comments.