Bifrost/src/Bifrost.Web/Configuration/DeskOptions.cs
Heimdall 47db4f83f2 desk: the answer route on credential and door ports (H3a)
POST /api/desk/{mind}/answers behind one pipeline: raw target, bearer, audience,
scope, Active membership, body shape, {mind} lookup, door. Mapped only when the
Desk section exists; a configured desk without its credential and door ports
stops startup. Stale_fork answers carry the current card read through the door.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-10 19:28:52 +00:00

61 lines
2.3 KiB
C#

namespace Bifrost.Web.Configuration;
// The fork desk's configuration (CultLib fork-card.md, Transport; M1 and M5). Minds is an ARRAY, never a map keyed
// by Mind id: configuration keys are case-insensitive, so a keyed section would let GAMECULT-CHAT reach
// gamecult-chat. Account ids are lowercase D-format Guids. An empty Minds list means the desk is not configured.
public sealed class DeskOptions
{
public const string SectionName = "Desk";
public List<DeskMindOptions> Minds { get; init; } = [];
public DeskAttributionOptions Attribution { get; init; } = new();
// The one origin allowed to call /api/desk from a browser (exactly this, no credentials).
public string AllowedOrigin { get; init; } = string.Empty;
// The cap on an answer body, in bytes, judged before it is parsed.
public int MaxBodyBytes { get; init; } = 16 * 1024;
// What an `unavailable` document tells the client to wait.
public int RetryAfterSeconds { get; init; } = 30;
public bool IsConfigured => Minds.Count > 0;
}
public sealed class DeskMindOptions
{
public string Id { get; init; } = string.Empty;
// The full path of this Mind's socket on the peer-checked door. Bifrost only connects to it.
public string Socket { get; init; } = string.Empty;
// Accounts that may answer this Mind's forks. Sent to Huginn on every call; no request sets or extends it.
public List<string> Deciders { get; init; } = [];
}
// The authority a stamped answer carries (fork-card.md: Operator, Steward or Member), from configuration because
// Bifrost has no such roles: Operator is one account, Stewards a list, everyone else a Member.
public sealed class DeskAttributionOptions
{
public string? Operator { get; init; }
public List<string> Stewards { get; init; } = [];
public string AuthorityOf(string accountId)
{
if (!string.IsNullOrEmpty(Operator) && string.Equals(accountId, Operator, StringComparison.Ordinal))
{
return DeskAuthority.Operator;
}
return Stewards.Contains(accountId, StringComparer.Ordinal) ? DeskAuthority.Steward : DeskAuthority.Member;
}
}
public static class DeskAuthority
{
public const string Operator = "Operator";
public const string Steward = "Steward";
public const string Member = "Member";
}