feat: authStatus() — read-only hub auth-policy snapshot
Some checks failed
Security / Security check (push) Failing after 2s

Regenerated stubs for the additive AuthStatus HubAdmin RPC and a
typed wrapper: active validator, anonymous flag, token entries with
scope grants, rate limits and env-var set-state. Secret values never
cross the wire.

Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
This commit is contained in:
flemming-it 2026-07-15 00:50:21 +02:00
parent 3fe96c9487
commit 9141a91314
4 changed files with 420 additions and 0 deletions

View file

@ -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<AuthTokenInfo>? 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<AuthTokenInfo>(3, _omitFieldNames ? '' : 'tokens',
subBuilder: AuthTokenInfo.create)
..aOM<JwtValidatorInfo>(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<AuthStatusResponse>(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<AuthTokenInfo> 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<AuthTokenInfo>(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<JwtValidatorInfo>(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,

View file

@ -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);

View file

@ -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',