ConfigService

Base path /squiddy.v1.ConfigService/ · defined in proto/squiddy/v1/config.proto

ConfigService is the remote-configuration RPC. It replaces the legacy GET|POST /api/GetRemoteConfiguration JSON endpoint with a typed Connect contract shared verbatim by the Go server and the KMP clients.

Authentication

Calls carry the API key of the environment they read, in the X-API-Key header. The key determines the tenant, project and environment, so a client sends only its app_id: any addressing coordinate in the request is ignored in favour of the key's. A request without a valid key is rejected with unauthenticated.

Procedures

ProcedureSummary
GetRemoteConfigurationGetRemoteConfiguration resolves the per-app configuration for the requesting client.

GetRemoteConfiguration

POST /squiddy.v1.ConfigService/GetRemoteConfiguration

GetRemoteConfiguration resolves the per-app configuration for the requesting client. Application failures surface as connect.Error with a typed error detail (see US 11.1.3 / 11.2.3), never as an in-band status envelope.

Errors: internal, invalid_argument, not_found, unauthenticated

GetRemoteConfigurationRequest

Request of GetRemoteConfiguration.

GetRemoteConfigurationRequest mirrors domain.ClientRequestModel. The six identity fields are required (their presence/emptiness is enforced by domain.Validate, not by the wire type, so they are plain strings). lang_fingerprint is the opaque token the client echoes back from a previous response; it is optional so an absent token (never sent) maps to a nil *string and stays distinct from an empty string.

tenant, project and environment are the addressing coordinates that, together with app_id, locate a configuration in the multi-tenant store, so the same app_id can resolve to different configurations per tenant and environment. They are optional for backward compatibility: a legacy appID-only client omits them, and the server resolves the request against the default tenant/project/environment (see domain.Normalize).

BREAKING CHANGE (EPIC-2, US 2.2.2; extended in BUG #802): when the data-plane requires an API key, the tenant, project and environment are ALL derived from the authenticated key, not from the request — any client-supplied value for the three is ignored (closing the tenant-spoofing seam). A request without a valid X-API-Key is rejected with CodeUnauthenticated. app_id stays client-supplied, since one key serves every app in its environment. The three addressing fields remain on the wire because the anonymous data-plane — used in local dev when no control plane is configured — still honors the client-supplied coordinates as before. A key is bound to one environment and an environment belongs to exactly one project, so a keyed client can send app_id alone and still resolve its configuration; before BUG #802 omitting project defaulted it to "default" and returned not_found for a configuration that was published.

config_fingerprint (US 4.1.2) is the application-level equivalent of an HTTP If-None-Match: the opaque token the client received as config_fingerprint in a previous response. Connect calls travel over POST, where 304/If-None-Match have no meaning, so the conditional is expressed in the message itself. When the token still matches the freshly resolved configuration the server answers with a minimal not_modified response. It is optional so a client that never sends it (every legacy client) is distinct from one sending an empty token, and it always yields the full payload.

entity_id (US 4.5.1) is the percentage-rollout bucketing key: the stable identity of whoever is asking, which the server hashes to decide whether this client is inside the share of the audience a gradually-released configuration block targets. THE ENTITY-KEY CONTRACT, which a client must honour for a rollout to behave:

#FieldJSONTypePresenceConstraints
1app_idappIdstringsingular—
2app_bundle_idappBundleIdstringsingular—
3app_versionappVersionstringsingular—
4app_typeappTypestringsingular—
5app_modeappModestringsingular—
6app_languageappLanguagestringsingular—
7lang_fingerprintlangFingerprintstringoptional—
8custom_paramscustomParamsstringrepeated—
9tenanttenantstringoptional—
10projectprojectstringoptional—
11environmentenvironmentstringoptional—
12config_fingerprintconfigFingerprintstringoptional—
13entity_identityIdstringoptional—

GetRemoteConfigurationResponse

Response of GetRemoteConfiguration.

GetRemoteConfigurationResponse mirrors domain.ClientResponseModel, minus the legacy status/statusCode/statusMsg envelope (errors now travel through connect.Error). Scalar fields that are nullable pointers in the domain use optional to preserve presence semantics; message-typed fields already carry presence inherently. lang_fingerprint is emitted only alongside a languages payload (the SHA-256 token the client echoes on the next call so the server can omit languages when it still matches).

config_fingerprint and not_modified (US 4.1.2) are the response half of the conditional contract. config_fingerprint is the SHA-256 token of the whole resolved configuration and is emitted on every successful response, so the client can store it and echo it back as the request's config_fingerprint. not_modified is true only on the minimal response the server returns when the echoed token still matches: in that case every heavy block (server_api, app_update, info_point, languages) is omitted and the client keeps the copy it already holds. It is a plain bool because false — the ordinary full response — is the meaningful default for a client that ignores the field.

#FieldJSONTypePresenceConstraints
1hub_namehubNamestringoptional—
2hub_versionhubVersionstringoptional—
3config_versionconfigVersionstringoptional—
4server_apiserverApiServerApisingular—
5app_updateappUpdateAppUpdatesingular—
6info_pointinfoPointInfoPointsingular—
7languageslanguagesLanguagessingular—
8lang_fingerprintlangFingerprintstringoptional—
9config_fingerprintconfigFingerprintstringoptional—
10not_modifiednotModifiedboolsingular—

Types

ServerApi

ServerApi is the resolved server-API block (domain.ServerApi). server_settings is a repeated message rather than optional: when populated it is the full list, and an empty list means "no settings", matching the domain slice that is never null.

#FieldJSONTypePresenceConstraints
1base_urlbaseUrlstringoptional—
2modemodestringoptional—
3api_versionapiVersionstringoptional—
4active_debugactiveDebugboolsingular—
5server_settingsserverSettingsServerSettingrepeated—

ServerSetting

ServerSetting is a single key/value server setting echoed to the client (domain.ServerSetting). All four fields are always present.

#FieldJSONTypePresenceConstraints
1keykeystringsingular—
2valuevaluestringsingular—
3typetypestringsingular—
4actionactionstringsingular—

AppUpdate

AppUpdate is the resolved app-update block (domain.ClientAppUpdate): a required flag plus an optional localized title/message.

#FieldJSONTypePresenceConstraints
1requiredrequiredboolsingular—
2titletitlestringoptional—
3messagemessagestringoptional—

InfoPoint

InfoPoint is the resolved info-point block (domain.ClientInFoPoint): a halt flag plus an optional localized title/message.

#FieldJSONTypePresenceConstraints
1halthaltboolsingular—
2titletitlestringoptional—
3messagemessagestringoptional—

Languages

Languages carries the default language and the language dictionary list (domain.Languages). lang_list models the domain shape []map[string][]map[string]string through a wrapper hierarchy because proto3 forbids both repeated map and a map whose value is itself a map:
  Languages -> repeated LangBlock -> map<string, StringMapList>
            -> repeated StringMap -> map<string, string>.

#FieldJSONTypePresenceConstraints
1default_langdefaultLangstringsingular—
2lang_listlangListLangBlockrepeated—

LangBlock

LangBlock is one element of the domain langList outer slice: a map[string][]map[string]string. The repeated-of-maps value is wrapped in StringMapList because a proto3 map value may not itself be a map or repeated.

#FieldJSONTypePresenceConstraints
1entriesentriesmap<string, StringMapList>map—

StringMapList

StringMapList wraps the domain []map[string]string (a list of string maps), since a proto3 map value cannot be a repeated field directly.

#FieldJSONTypePresenceConstraints
1itemsitemsStringMaprepeated—

StringMap

StringMap wraps a single domain map[string]string; the wrapper lets it be used as a repeated element inside StringMapList.

#FieldJSONTypePresenceConstraints
1entriesentriesmap<string, string>map—

ErrorDetail

ErrorDetail is the typed payload attached to a connect.Error so clients can react to an application failure without parsing an in-band status envelope. It replaces the legacy ko response (statusCode/statusMsg/...): exception_code carries the original application code (domain.ExceptionCode, 5000-5003), while the connect.Code on the error itself conveys the idiomatic status (see the mapping in package connectapi). config_version is optional because it is known only for failures raised after the configuration was loaded (e.g. NullArgument); hub_name is always set.

hub_version is DEPRECATED and no longer stamped on errors (BUG #803): on a failure it only named the build of the retired .NET assembly the message format came from, which tells whoever is reading the error nothing actionable. The field is kept on the wire, always empty, so existing clients do not break. The successful response still carries hub_version, where it is part of the contract.

#FieldJSONTypePresenceConstraints
1exception_codeexceptionCodeint32singular—
2config_versionconfigVersionstringoptional—
3hub_namehubNamestringsingular—
4hub_versionhubVersionstringsingulardeprecated