The LA3 Opus review process note was right: the contract both sides implement lived only in orchestrator prompts, which is exactly the drift mode the pin exists to prevent (and it produced the paths-key CRITICAL). The schema, field rules, probe-mode discriminator, and status vocabulary are now a binding plan section; amendments change this text first, implementations second. Ledger: LA3 fix round dispatched. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
65 lines
1.9 KiB
C#
65 lines
1.9 KiB
C#
using System.Security.Cryptography;
|
|
|
|
namespace AcDream.App.Credentials;
|
|
|
|
/// <summary>
|
|
/// Campaign LA slice LA1: retains a resolved <c>--session-config</c>
|
|
/// credential in erasable memory — the App-side mirror of
|
|
/// <c>AcDream.Headless.Credentials.HeadlessCredentialSecret</c> (that type is
|
|
/// internal to the Headless project, so this is a minimal, independent port
|
|
/// rather than a shared reference). The network boundary still requires one
|
|
/// short-lived immutable string; callers must not retain that value beyond
|
|
/// constructing the connect request.
|
|
/// </summary>
|
|
internal sealed class AppCredentialSecret : IDisposable
|
|
{
|
|
private char[]? _buffer;
|
|
|
|
internal AppCredentialSecret(string referenceId, ReadOnlySpan<char> value)
|
|
{
|
|
ArgumentException.ThrowIfNullOrWhiteSpace(referenceId);
|
|
if (value.IsEmpty)
|
|
{
|
|
throw new AppCredentialException(
|
|
$"Credential '{referenceId}' resolved to an empty secret.");
|
|
}
|
|
|
|
ReferenceId = referenceId;
|
|
_buffer = value.ToArray();
|
|
}
|
|
|
|
internal string ReferenceId { get; }
|
|
internal bool IsDisposed => _buffer is null;
|
|
|
|
internal string Reveal()
|
|
{
|
|
ObjectDisposedException.ThrowIf(_buffer is null, this);
|
|
return new string(_buffer);
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
char[]? buffer = Interlocked.Exchange(ref _buffer, null);
|
|
if (buffer is null)
|
|
return;
|
|
CryptographicOperations.ZeroMemory(
|
|
System.Runtime.InteropServices.MemoryMarshal.AsBytes(
|
|
buffer.AsSpan()));
|
|
}
|
|
|
|
public override string ToString() =>
|
|
$"[redacted:{ReferenceId}]";
|
|
}
|
|
|
|
internal sealed class AppCredentialException : Exception
|
|
{
|
|
internal AppCredentialException(string message)
|
|
: base(message)
|
|
{
|
|
}
|
|
|
|
internal AppCredentialException(string message, Exception innerException)
|
|
: base(message, innerException)
|
|
{
|
|
}
|
|
}
|