diff --git a/lib/src/generated/chain/v1/hub.pb.dart b/lib/src/generated/chain/v1/hub.pb.dart index 29100c7..523f417 100644 --- a/lib/src/generated/chain/v1/hub.pb.dart +++ b/lib/src/generated/chain/v1/hub.pb.dart @@ -7408,6 +7408,301 @@ class ReloadAuthResponse extends $pb.GeneratedMessage { void clearTokenCount() => $_clearField(1); } +class AuthStatusResponse extends $pb.GeneratedMessage { + factory AuthStatusResponse({ + $core.String? validator, + $core.bool? anonymousAllowed, + $core.Iterable? tokens, + JwtValidatorInfo? jwt, + }) { + final result = create(); + if (validator != null) result.validator = validator; + if (anonymousAllowed != null) result.anonymousAllowed = anonymousAllowed; + if (tokens != null) result.tokens.addAll(tokens); + if (jwt != null) result.jwt = jwt; + return result; + } + + AuthStatusResponse._(); + + factory AuthStatusResponse.fromBuffer($core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory AuthStatusResponse.fromJson($core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'AuthStatusResponse', + package: const $pb.PackageName(_omitMessageNames ? '' : 'chain.v1'), + createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'validator') + ..aOB(2, _omitFieldNames ? '' : 'anonymousAllowed') + ..pPM(3, _omitFieldNames ? '' : 'tokens', + subBuilder: AuthTokenInfo.create) + ..aOM(4, _omitFieldNames ? '' : 'jwt', + subBuilder: JwtValidatorInfo.create) + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + AuthStatusResponse clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + AuthStatusResponse copyWith(void Function(AuthStatusResponse) updates) => + super.copyWith((message) => updates(message as AuthStatusResponse)) + as AuthStatusResponse; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static AuthStatusResponse create() => AuthStatusResponse._(); + @$core.override + AuthStatusResponse createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static AuthStatusResponse getDefault() => _defaultInstance ??= + $pb.GeneratedMessage.$_defaultFor(create); + static AuthStatusResponse? _defaultInstance; + + /// Active validator kind: "static" or "jwt-rs256". + @$pb.TagNumber(1) + $core.String get validator => $_getSZ(0); + @$pb.TagNumber(1) + set validator($core.String value) => $_setString(0, value); + @$pb.TagNumber(1) + $core.bool hasValidator() => $_has(0); + @$pb.TagNumber(1) + void clearValidator() => $_clearField(1); + + /// True when the hub accepts anonymous calls (static validator + /// with an empty `auth.tokens:` list — the local-dev default). + @$pb.TagNumber(2) + $core.bool get anonymousAllowed => $_getBF(1); + @$pb.TagNumber(2) + set anonymousAllowed($core.bool value) => $_setBool(1, value); + @$pb.TagNumber(2) + $core.bool hasAnonymousAllowed() => $_has(1); + @$pb.TagNumber(2) + void clearAnonymousAllowed() => $_clearField(2); + + /// Static token entries; empty for the jwt-rs256 validator. + @$pb.TagNumber(3) + $pb.PbList get tokens => $_getList(2); + + /// jwt-rs256 parameters; unset for the static validator. + @$pb.TagNumber(4) + JwtValidatorInfo get jwt => $_getN(3); + @$pb.TagNumber(4) + set jwt(JwtValidatorInfo value) => $_setField(4, value); + @$pb.TagNumber(4) + $core.bool hasJwt() => $_has(3); + @$pb.TagNumber(4) + void clearJwt() => $_clearField(4); + @$pb.TagNumber(4) + JwtValidatorInfo ensureJwt() => $_ensure(3); +} + +class AuthTokenInfo extends $pb.GeneratedMessage { + factory AuthTokenInfo({ + $core.String? name, + $core.String? tokenEnv, + $core.bool? envSet, + $core.Iterable<$core.String>? scopes, + $core.int? rateLimitPerMinute, + }) { + final result = create(); + if (name != null) result.name = name; + if (tokenEnv != null) result.tokenEnv = tokenEnv; + if (envSet != null) result.envSet = envSet; + if (scopes != null) result.scopes.addAll(scopes); + if (rateLimitPerMinute != null) + result.rateLimitPerMinute = rateLimitPerMinute; + return result; + } + + AuthTokenInfo._(); + + factory AuthTokenInfo.fromBuffer($core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory AuthTokenInfo.fromJson($core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'AuthTokenInfo', + package: const $pb.PackageName(_omitMessageNames ? '' : 'chain.v1'), + createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'name') + ..aOS(2, _omitFieldNames ? '' : 'tokenEnv') + ..aOB(3, _omitFieldNames ? '' : 'envSet') + ..pPS(4, _omitFieldNames ? '' : 'scopes') + ..aI(5, _omitFieldNames ? '' : 'rateLimitPerMinute', + fieldType: $pb.PbFieldType.OU3) + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + AuthTokenInfo clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + AuthTokenInfo copyWith(void Function(AuthTokenInfo) updates) => + super.copyWith((message) => updates(message as AuthTokenInfo)) + as AuthTokenInfo; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static AuthTokenInfo create() => AuthTokenInfo._(); + @$core.override + AuthTokenInfo createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static AuthTokenInfo getDefault() => _defaultInstance ??= + $pb.GeneratedMessage.$_defaultFor(create); + static AuthTokenInfo? _defaultInstance; + + /// Operator-friendly name as it appears in audit-log lines. + @$pb.TagNumber(1) + $core.String get name => $_getSZ(0); + @$pb.TagNumber(1) + set name($core.String value) => $_setString(0, value); + @$pb.TagNumber(1) + $core.bool hasName() => $_has(0); + @$pb.TagNumber(1) + void clearName() => $_clearField(1); + + /// Name of the environment variable holding the secret. The + /// value itself never crosses the wire. + @$pb.TagNumber(2) + $core.String get tokenEnv => $_getSZ(1); + @$pb.TagNumber(2) + set tokenEnv($core.String value) => $_setString(1, value); + @$pb.TagNumber(2) + $core.bool hasTokenEnv() => $_has(1); + @$pb.TagNumber(2) + void clearTokenEnv() => $_clearField(2); + + /// Whether that environment variable is currently set in the + /// daemon's environment — the common rotation footgun. + @$pb.TagNumber(3) + $core.bool get envSet => $_getBF(2); + @$pb.TagNumber(3) + set envSet($core.bool value) => $_setBool(2, value); + @$pb.TagNumber(3) + $core.bool hasEnvSet() => $_has(2); + @$pb.TagNumber(3) + void clearEnvSet() => $_clearField(3); + + /// Scope grants, verbatim from config (coarse scopes like + /// `read`/`execute`/`admin` and fine-grained T5 patterns like + /// `execute:llm.*`). + @$pb.TagNumber(4) + $pb.PbList<$core.String> get scopes => $_getList(3); + + /// Requests-per-minute quota; 0 = unlimited. + @$pb.TagNumber(5) + $core.int get rateLimitPerMinute => $_getIZ(4); + @$pb.TagNumber(5) + set rateLimitPerMinute($core.int value) => $_setUnsignedInt32(4, value); + @$pb.TagNumber(5) + $core.bool hasRateLimitPerMinute() => $_has(4); + @$pb.TagNumber(5) + void clearRateLimitPerMinute() => $_clearField(5); +} + +class JwtValidatorInfo extends $pb.GeneratedMessage { + factory JwtValidatorInfo({ + $core.String? keySource, + $core.String? audience, + $core.String? issuer, + $core.String? scopeClaim, + }) { + final result = create(); + if (keySource != null) result.keySource = keySource; + if (audience != null) result.audience = audience; + if (issuer != null) result.issuer = issuer; + if (scopeClaim != null) result.scopeClaim = scopeClaim; + return result; + } + + JwtValidatorInfo._(); + + factory JwtValidatorInfo.fromBuffer($core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory JwtValidatorInfo.fromJson($core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'JwtValidatorInfo', + package: const $pb.PackageName(_omitMessageNames ? '' : 'chain.v1'), + createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'keySource') + ..aOS(2, _omitFieldNames ? '' : 'audience') + ..aOS(3, _omitFieldNames ? '' : 'issuer') + ..aOS(4, _omitFieldNames ? '' : 'scopeClaim') + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + JwtValidatorInfo clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + JwtValidatorInfo copyWith(void Function(JwtValidatorInfo) updates) => + super.copyWith((message) => updates(message as JwtValidatorInfo)) + as JwtValidatorInfo; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static JwtValidatorInfo create() => JwtValidatorInfo._(); + @$core.override + JwtValidatorInfo createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static JwtValidatorInfo getDefault() => _defaultInstance ??= + $pb.GeneratedMessage.$_defaultFor(create); + static JwtValidatorInfo? _defaultInstance; + + /// Where the RSA public key comes from: "inline-pem" when the + /// key is embedded in config, else the configured file path. + @$pb.TagNumber(1) + $core.String get keySource => $_getSZ(0); + @$pb.TagNumber(1) + set keySource($core.String value) => $_setString(0, value); + @$pb.TagNumber(1) + $core.bool hasKeySource() => $_has(0); + @$pb.TagNumber(1) + void clearKeySource() => $_clearField(1); + + /// Required `aud` claim; empty = not checked. + @$pb.TagNumber(2) + $core.String get audience => $_getSZ(1); + @$pb.TagNumber(2) + set audience($core.String value) => $_setString(1, value); + @$pb.TagNumber(2) + $core.bool hasAudience() => $_has(1); + @$pb.TagNumber(2) + void clearAudience() => $_clearField(2); + + /// Required `iss` claim; empty = not checked. + @$pb.TagNumber(3) + $core.String get issuer => $_getSZ(2); + @$pb.TagNumber(3) + set issuer($core.String value) => $_setString(2, value); + @$pb.TagNumber(3) + $core.bool hasIssuer() => $_has(2); + @$pb.TagNumber(3) + void clearIssuer() => $_clearField(3); + + /// Claim carrying the caller's scopes. + @$pb.TagNumber(4) + $core.String get scopeClaim => $_getSZ(3); + @$pb.TagNumber(4) + set scopeClaim($core.String value) => $_setString(3, value); + @$pb.TagNumber(4) + $core.bool hasScopeClaim() => $_has(3); + @$pb.TagNumber(4) + void clearScopeClaim() => $_clearField(4); +} + class McpClientConfigEntry extends $pb.GeneratedMessage { factory McpClientConfigEntry({ $core.String? name, diff --git a/lib/src/generated/chain/v1/hub.pbgrpc.dart b/lib/src/generated/chain/v1/hub.pbgrpc.dart index 26af5e7..6a13099 100644 --- a/lib/src/generated/chain/v1/hub.pbgrpc.dart +++ b/lib/src/generated/chain/v1/hub.pbgrpc.dart @@ -583,6 +583,20 @@ class HubAdminClient extends $grpc.Client { return $createUnaryCall(_$reloadAuth, request, options: options); } + /// Read-only snapshot of the authentication policy as persisted + /// in the operator config, for operator surfaces (Studio + /// Settings → Security). Never carries secret material: token + /// entries expose the env-var NAME and whether that variable is + /// currently set — never the value; the jwt-rs256 validator + /// exposes its key SOURCE (inline / file path), never the key. + /// Admin-scoped like ReloadAuth. + $grpc.ResponseFuture<$0.AuthStatusResponse> authStatus( + $1.Empty request, { + $grpc.CallOptions? options, + }) { + return $createUnaryCall(_$authStatus, request, options: options); + } + /// List host services declared in the operator config. Each /// entry carries name + endpoint; reachability is operator-side /// (on-demand `chain service status` or Studio probe). @@ -978,6 +992,11 @@ class HubAdminClient extends $grpc.Client { '/chain.v1.HubAdmin/ReloadAuth', ($1.Empty value) => value.writeToBuffer(), $0.ReloadAuthResponse.fromBuffer); + static final _$authStatus = + $grpc.ClientMethod<$1.Empty, $0.AuthStatusResponse>( + '/chain.v1.HubAdmin/AuthStatus', + ($1.Empty value) => value.writeToBuffer(), + $0.AuthStatusResponse.fromBuffer); static final _$listServices = $grpc.ClientMethod<$1.Empty, $0.ServiceList>( '/chain.v1.HubAdmin/ListServices', ($1.Empty value) => value.writeToBuffer(), @@ -1333,6 +1352,13 @@ abstract class HubAdminServiceBase extends $grpc.Service { false, ($core.List<$core.int> value) => $1.Empty.fromBuffer(value), ($0.ReloadAuthResponse value) => value.writeToBuffer())); + $addMethod($grpc.ServiceMethod<$1.Empty, $0.AuthStatusResponse>( + 'AuthStatus', + authStatus_Pre, + false, + false, + ($core.List<$core.int> value) => $1.Empty.fromBuffer(value), + ($0.AuthStatusResponse value) => value.writeToBuffer())); $addMethod($grpc.ServiceMethod<$1.Empty, $0.ServiceList>( 'ListServices', listServices_Pre, @@ -1765,6 +1791,14 @@ abstract class HubAdminServiceBase extends $grpc.Service { $async.Future<$0.ReloadAuthResponse> reloadAuth( $grpc.ServiceCall call, $1.Empty request); + $async.Future<$0.AuthStatusResponse> authStatus_Pre( + $grpc.ServiceCall $call, $async.Future<$1.Empty> $request) async { + return authStatus($call, await $request); + } + + $async.Future<$0.AuthStatusResponse> authStatus( + $grpc.ServiceCall call, $1.Empty request); + $async.Future<$0.ServiceList> listServices_Pre( $grpc.ServiceCall $call, $async.Future<$1.Empty> $request) async { return listServices($call, await $request); diff --git a/lib/src/generated/chain/v1/hub.pbjson.dart b/lib/src/generated/chain/v1/hub.pbjson.dart index de6a4d5..279faf7 100644 --- a/lib/src/generated/chain/v1/hub.pbjson.dart +++ b/lib/src/generated/chain/v1/hub.pbjson.dart @@ -1791,6 +1791,85 @@ const ReloadAuthResponse$json = { final $typed_data.Uint8List reloadAuthResponseDescriptor = $convert.base64Decode( 'ChJSZWxvYWRBdXRoUmVzcG9uc2USHwoLdG9rZW5fY291bnQYASABKA1SCnRva2VuQ291bnQ='); +@$core.Deprecated('Use authStatusResponseDescriptor instead') +const AuthStatusResponse$json = { + '1': 'AuthStatusResponse', + '2': [ + {'1': 'validator', '3': 1, '4': 1, '5': 9, '10': 'validator'}, + { + '1': 'anonymous_allowed', + '3': 2, + '4': 1, + '5': 8, + '10': 'anonymousAllowed' + }, + { + '1': 'tokens', + '3': 3, + '4': 3, + '5': 11, + '6': '.chain.v1.AuthTokenInfo', + '10': 'tokens' + }, + { + '1': 'jwt', + '3': 4, + '4': 1, + '5': 11, + '6': '.chain.v1.JwtValidatorInfo', + '10': 'jwt' + }, + ], +}; + +/// Descriptor for `AuthStatusResponse`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List authStatusResponseDescriptor = $convert.base64Decode( + 'ChJBdXRoU3RhdHVzUmVzcG9uc2USHAoJdmFsaWRhdG9yGAEgASgJUgl2YWxpZGF0b3ISKwoRYW' + '5vbnltb3VzX2FsbG93ZWQYAiABKAhSEGFub255bW91c0FsbG93ZWQSLwoGdG9rZW5zGAMgAygL' + 'MhcuY2hhaW4udjEuQXV0aFRva2VuSW5mb1IGdG9rZW5zEiwKA2p3dBgEIAEoCzIaLmNoYWluLn' + 'YxLkp3dFZhbGlkYXRvckluZm9SA2p3dA=='); + +@$core.Deprecated('Use authTokenInfoDescriptor instead') +const AuthTokenInfo$json = { + '1': 'AuthTokenInfo', + '2': [ + {'1': 'name', '3': 1, '4': 1, '5': 9, '10': 'name'}, + {'1': 'token_env', '3': 2, '4': 1, '5': 9, '10': 'tokenEnv'}, + {'1': 'env_set', '3': 3, '4': 1, '5': 8, '10': 'envSet'}, + {'1': 'scopes', '3': 4, '4': 3, '5': 9, '10': 'scopes'}, + { + '1': 'rate_limit_per_minute', + '3': 5, + '4': 1, + '5': 13, + '10': 'rateLimitPerMinute' + }, + ], +}; + +/// Descriptor for `AuthTokenInfo`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List authTokenInfoDescriptor = $convert.base64Decode( + 'Cg1BdXRoVG9rZW5JbmZvEhIKBG5hbWUYASABKAlSBG5hbWUSGwoJdG9rZW5fZW52GAIgASgJUg' + 'h0b2tlbkVudhIXCgdlbnZfc2V0GAMgASgIUgZlbnZTZXQSFgoGc2NvcGVzGAQgAygJUgZzY29w' + 'ZXMSMQoVcmF0ZV9saW1pdF9wZXJfbWludXRlGAUgASgNUhJyYXRlTGltaXRQZXJNaW51dGU='); + +@$core.Deprecated('Use jwtValidatorInfoDescriptor instead') +const JwtValidatorInfo$json = { + '1': 'JwtValidatorInfo', + '2': [ + {'1': 'key_source', '3': 1, '4': 1, '5': 9, '10': 'keySource'}, + {'1': 'audience', '3': 2, '4': 1, '5': 9, '10': 'audience'}, + {'1': 'issuer', '3': 3, '4': 1, '5': 9, '10': 'issuer'}, + {'1': 'scope_claim', '3': 4, '4': 1, '5': 9, '10': 'scopeClaim'}, + ], +}; + +/// Descriptor for `JwtValidatorInfo`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List jwtValidatorInfoDescriptor = $convert.base64Decode( + 'ChBKd3RWYWxpZGF0b3JJbmZvEh0KCmtleV9zb3VyY2UYASABKAlSCWtleVNvdXJjZRIaCghhdW' + 'RpZW5jZRgCIAEoCVIIYXVkaWVuY2USFgoGaXNzdWVyGAMgASgJUgZpc3N1ZXISHwoLc2NvcGVf' + 'Y2xhaW0YBCABKAlSCnNjb3BlQ2xhaW0='); + @$core.Deprecated('Use mcpClientConfigEntryDescriptor instead') const McpClientConfigEntry$json = { '1': 'McpClientConfigEntry', diff --git a/lib/src/hub_client.dart b/lib/src/hub_client.dart index 233e7c4..ae2f0ba 100644 --- a/lib/src/hub_client.dart +++ b/lib/src/hub_client.dart @@ -68,6 +68,9 @@ typedef ListSystemAiCuratedModelsResponse = typedef CuratedModel = pb.CuratedModel; typedef ClearEventLogResponse = pb.ClearEventLogResponse; typedef DaemonPathsResponse = pb.DaemonPathsResponse; +typedef AuthStatusResponse = pb.AuthStatusResponse; +typedef AuthTokenInfo = pb.AuthTokenInfo; +typedef JwtValidatorInfo = pb.JwtValidatorInfo; typedef UninstallModuleResponse = pb.UninstallModuleResponse; typedef FetchModuleDocsResponse = pb.FetchModuleDocsResponse; typedef GetInstalledModuleDocsResponse = pb.GetInstalledModuleDocsResponse; @@ -251,6 +254,15 @@ class HubClient { return r.tokenCount; } + /// Read-only snapshot of the hub's authentication policy as + /// persisted in the operator config: active validator, whether + /// anonymous calls are accepted, and the static token entries + /// (names, scope grants, env-var name + set-flag — never the + /// secret value). Admin-scoped like [reloadAuth]. + Future authStatus() { + return _admin.authStatus(Empty()); + } + /// Server-streaming audit-log subscription. The hub first /// replays up to [backfill] historical events (oldest-last so /// the receiver sees them in chronological order), then keeps