Skip to content

feat(secrets): write-only secret entry in the portal — InstanceSecrets, the secret control, and client-b's announcement key without a vault act - #5791

Merged
rbuergi merged 6 commits into
mainfrom
feat/instance-secrets-announcement-key
Sep 27, 2026
Merged

rbuergi merged 6 commits into
mainfrom
feat/instance-secrets-announcement-key

Conversation

@rbuergi

@rbuergi rbuergi commented Sep 27, 2026 •

Copy link
Copy Markdown
Contributor

What

A secret is entered through a write-only page, never minted by an operator in a vault. This is the core half, and the first user is client-b's self-update announcement key: client-b is customer-administered, and its administrator must be able to enter the key with no az and no kubectl. Refs #5757, #5775.

Platform pieces, which other secrets reuse

  • SecretFingerprint (MeshWeaver.Mesh.Security) is the fleet-wide rule, byte for byte the same as the operator's mw-fp vault tag. It is sha256: plus 12 hex digits, or withheld:low-entropy when floor(len × log2(alphabet)) < 128. It also offers Generate(), a 256-bit hex key.
  • SecretStatus: the only shape a UI reads back. Its JSON names are the operator's kv_status fields (name, present, enabled, created, updated, expires, deleted, recoverableUntil, fingerprint, setBy, setAt, source). The Key Vault agent confirmed deleted is a bool and enabled is null when absent.
  • InstanceSecrets (MeshWeaver.Graph.Configuration): the instance's own encrypted store.
    • Each value is stored at Admin/Secret-{key} as enc: ciphertext from IProviderKeyProtector. A value the protector cannot encrypt is refused, never stored in the clear.
    • Only keys a reader registered as a SLOT can be set (AddInstanceSecretSlot). A :* slot admits exactly one more segment and never the section itself, so the fleet inbox secret cannot be replaced.
    • Rights are checked first as the caller (IsGlobalAdmin), and the write then runs as system.
    • Values are read LIVE through InstanceSecretCatalog, one query subscription per mesh, so no restart is needed.
    • Verbs: Set, Generate (returns the value once), Disable (a tombstone that also suppresses a mounted copy), Enable, Delete and Recover (with a recovery window).
    • Rotation keeps the previous value verifying until the first verified use of the new one, or at most 14 days.
    • RecordUse records the last use.
  • WriteOnlySecretSection / SecretInventorySection: the platform's one write-only control.
    • It shows a password box that is cleared as soon as it is read, and the status only.
    • A generated value is shown once in a dialog.
    • Delete asks for confirmation.
    • It is composed from framework controls (Stack, Markdown, TextField, Button, Dialog, DataGrid).

The announcement key

  • WebhookInbox now verifies per-sender keys from the portal as well as from configuration, and a rotation's previous key (MatchSender).
    • The header X-MeshWeaver-Verify-Only: true asks for verify only, store nothing. That is what "Test connection" uses.
    • The 200 answer now carries sender, the name of the key that verified (never its value).
  • SelfUpdateHandover signs with the portal-entered key when there is one, and otherwise with the mounted Hosting:ControlInbox:Secret.
    • It records each announcement's result as the key's last use.
    • It adds Test(), which sends a signed, verify-only test and returns Match (as whom) or Mismatch.
  • Settings ▸ Control lane (admin-gated) is where the deployment's own admin enters the key, sees its fingerprint, and presses Test connection.

Docs

  • Doc/Architecture/InstanceSecrets is new.
  • Doc/Architecture/SelfUpdateAnnouncementKey gains the portal flow and a client-facing step-by-step section.
  • Both are listed in the Architecture topic map.
  • The page's "Owner commands" section is deliberately untouched here, because the Key Vault agent's PR rewrites it.

Verification

  • Release with -warnaserror, one project per build: MeshWeaver.Graph, Memex.Portal.Shared, MeshWeaver.Graph.Test, Memex.Portal.Shared.Test, MeshWeaver.Documentation.Test, MeshWeaver.Messaging.Hub.Test. All report 0 warnings and 0 errors.
  • InstanceSecretsTest passes 22/22. It covers:
    • a saved key is stored enc:, resolved live, and never appears in any log line;
    • a non-admin and a non-slot key are refused, and nothing is written;
    • a portal-issued sender key verifies on the next delivery and records the use;
    • verify-only stores nothing and names the sender;
    • a rotation accepts both keys until the new one is used;
    • disabling also stops a mounted key;
    • Generate / Disable / Enable / Delete / Recover;
    • the fingerprint entropy rule.
    • Negative control: with previous-key matching removed, the rotation test fails.
  • ControlLaneKeyRoundTripTest passes 8/8. It runs the whole loop in one mesh: the control issues a key, client-b saves it, the fingerprints are equal, and Test() answers verified as client-b, with the result recorded on both ends. A mistyped key reads as a 401 mismatch, nothing is stored, and the key never reaches a log.
  • WebhookInboxTest passes 14/14 (unchanged). The Settings/SelfUpdate suites pass: 14 and 134.
  • MeshWeaver.Documentation.Test passes 649/649, including RootedRxConnectionRatchetGuard, which caught a RefCount that is now removed.

Cross-repo

Pairs-with: none — nothing public is removed; types and members are only added (DeliveryResult.VerifyOnly is an init property).
Implementers: none — no interface member is added.
Mirror-sync: npm run sync:i18n -- --ref <merged core sha> is carried by the MeshWeaver.Plugins companion PR (the record-page Announcement key panel), which follows once this is sealed. It adds keys only: settings.controlLane, ui.controlLane*, hosting.annKey.*, secret.*.

After deploy, nothing needs recycling: the settings tab and the catalog are compiled platform code, and they arrive with the image.

🤖 Generated with Claude Code

…s, the secret control, and the announcement key without a vault act

A global administrator ENTERS a secret in the portal instead of an operator minting it:

- SecretFingerprint (fleet rule: sha256:+12 hex, withheld below 128 estimated bits) and
  SecretStatus (the operator's kv_status field names) in Mesh.Contract.
- InstanceSecrets: slot-registered configuration keys stored enc: in Admin/Secret-{key},
  rights checked as the caller then written as system, read LIVE through a mesh-scoped
  catalog (no restart). Set / Generate-once / Disable / Enable / Delete / Recover, rotation
  with the previous value still verifying, last-use recording.
- WriteOnlySecretSection / SecretInventorySection: the platform's one write-only control.
- WebhookInbox: per-sender keys from the portal as well as configuration, a rotation's
  previous key, and a verify-only test header that stores nothing.
- SelfUpdateHandover signs with the portal-set key when there is one, records each
  announcement's result, and offers Test() (signed, verify-only).
- Settings > Control lane: enter the control instance's announcement key, see its
  fingerprint, Test connection.
- Docs: Architecture/InstanceSecrets (new) and SelfUpdateAnnouncementKey (portal flow,
  client-facing steps).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings September 27, 2026 07:48
…og then shows only the fingerprint and the note

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Sep 27, 2026 •

Copy link
Copy Markdown
Contributor

Test Results (shard 3)

460 tests  ±0   460 ✅ ±0   55s ⏱️ -1s
  3 suites ±0     0 💤 ±0 
  3 files   ±0     0 ❌ ±0 

Results for commit f51a551. ± Comparison against base commit 2bb14d8.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Sep 27, 2026 •

Copy link
Copy Markdown
Contributor

Test Results (shard 0)

  1 files  ±0    1 suites  ±0   2m 45s ⏱️ -29s
348 tests ±0  348 ✅ ±0  0 💤 ±0  0 ❌ ±0 
352 runs  ±0  352 ✅ ±0  0 💤 ±0  0 ❌ ±0 

Results for commit f51a551. ± Comparison against base commit 2bb14d8.

♻️ This comment has been updated with latest results.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Unresolved critical and moderate security, authorization, catalog, and rotation issues remain.

Review effort: Lite
Findings: 8 High severity · 5 Medium severity · 8 Low severity

Open (21)

And 1 more that still need to be addressed.

What changed in this PR

Adds encrypted, write-only instance secrets for portal-managed self-update announcement keys, with Control lane UI, webhook verification, localization, documentation, and tests.

Changes:

  • Adds secret storage, fingerprinting, rotation, lifecycle operations, and live cataloging.
  • Integrates portal keys with announcement signing and webhook verification.
  • Adds localized UI, documentation, and integration coverage.
File Description
test/​MeshWeaver.Graph.Test/​InstanceSecretsTest.cs Secret storage and behavior tests
test/​Memex.Portal.Shared.Test/​PlatformSettingsTabSeedTest.cs Settings-tab seed coverage
test/​Memex.Portal.Shared.Test/​ControlLaneKeyRoundTripTest.cs End-to-end key round-trip tests
src/​MeshWeaver.Messaging.Hub/​Localization/​strings.en.json English localization
src/​MeshWeaver.Messaging.Hub/​Localization/​strings.de.json German localization
src/​MeshWeaver.Mesh.Contract/​Security/​SecretStatus.cs Secret status contract
src/​MeshWeaver.Mesh.Contract/​Security/​SecretFingerprint.cs Fingerprint contract and generation
src/​MeshWeaver.Graph/​Configuration/​WriteOnlySecretSection.cs Write-only secret UI
src/​MeshWeaver.Graph/​Configuration/​WebhookEventNodeType.cs Sender-key and verify-only handling
src/​MeshWeaver.Graph/​Configuration/​InstanceSecrets.cs Encrypted secret storage and live catalog
src/​MeshWeaver.Graph/​Configuration/​GraphConfigurationExtensions.cs Secret type registration
src/​MeshWeaver.Documentation/​Data/​Architecture/​SelfUpdateAnnouncementKey.md Announcement-key workflow documentation
src/​MeshWeaver.Documentation/​Data/​Architecture/​InstanceSecrets.md Instance-secret documentation
src/​MeshWeaver.Documentation/​Data/​Architecture.md Architecture topic map
memex/​Memex.Portal.Shared/​Settings/​PlatformSettingsTabAreas.cs Settings-tab registration
memex/​Memex.Portal.Shared/​Settings/​ControlLaneSettingsTab.cs Control lane settings UI
memex/​Memex.Portal.Shared/​SelfUpdate/​SelfUpdateHandover.cs Portal-key signing and testing
memex/​Memex.Portal.Shared/​MemexConfiguration.cs Secret slot registration
memex/​Memex.Portal.Shared/​Api/​WebhookInboxEndpoints.cs Verify-only response contract

💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Name = "Instance Secret",
Icon = "/static/NodeTypeIcons/key.svg",
// Never offered for creation, never indexed as context: the content is ciphertext and metadata.
ExcludeFromContext = new HashSet<string> { "search", "create", "content" },

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 99fa68b. The new InstanceSecretWriteGuard is an owner-enforced INodeValidator that refuses any create, update or delete of an InstanceSecret node unless the writer is system or a hub, so the verbs are the only way in. The catalog also uses only enc:-tagged values now, so plain content that got past the guard would still never be used as a key. Both are pinned in A_direct_write_is_refused_and_a_plain_value_is_never_used.

};

/// <summary>The node id for <paramref name="configKey"/>: <c>Secret-</c> plus the key with <c>:</c> as <c>--</c>. Pure.</summary>
public static string IdOf(string configKey) => IdPrefix + configKey.Trim().Replace(":", "--");

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 99fa68b. Admits refuses any key containing -- (the id separator), so the encoding is injective. Pinned in A_key_that_would_collide_in_the_node_id_is_not_admitted.

};

/// <summary>The node id for <paramref name="configKey"/>: <c>Secret-</c> plus the key with <c>:</c> as <c>--</c>. Pure.</summary>
public static string IdOf(string configKey) => IdPrefix + configKey.Trim().Replace(":", "--");

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 99fa68b. IdOf lower-cases the key, so two spellings address one node. Pinned in Two_spellings_of_one_key_address_one_node.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(re-posted 2026-10-11; originally by Copilot on 2026-09-27)

Admits intentionally compares keys case-insensitively, and the catalog dictionary does too, but IdOf preserves casing. Thus Hosting:PlatformWebhookSecret:client-b and hosting:platformwebhooksecret:client-b are both admitted yet create different Admin/Secret-* nodes; later catalog folding makes them collide nondeterministically and can select the wrong ciphertext/status. Canonicalize the key before constructing the node id.

Comment on lines +251 to +255
var catalog = hub.ServiceProvider.GetService<InstanceSecretCatalog>();
if (catalog?.Entry(configKey) is { IsDeleted: false } entry)
return entry.IsDisabled ? null : entry.Current;
var value = hub.ServiceProvider.GetService<IConfiguration>()?[configKey];
return string.IsNullOrWhiteSpace(value) ? null : value;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 99fa68b. SelfUpdateHandover.Announce and Test now wait on InstanceSecretCatalog.WhenLoaded before they choose a route or a key, so a portal key or a portal disable is never skipped during the first listing.

string? stored;
try
{
stored = protector.Protect(value);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 99fa68b. Set refuses a value that starts with enc: before it calls the protector (secret.error.reservedPrefix). Pinned in A_value_carrying_the_encryption_tag_is_refused.

Comment on lines +294 to +295
if (status.SetAt is not null || status.SetBy is not null)
lines.Add(localize("secret.status.setBy", [Stamp(status.SetAt), status.SetBy ?? "?", status.Source ?? "?"]));

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 99fa68b. SourceLabel maps paste and generate to secret.source.*. An unknown value is shown as it is, because the vocabulary is open (policy open-vocabulary-string-constants: unknown stays unknown and is never folded into a known value).

Comment on lines +143 to +147
var stored = await Observable.Using(
() => Access.ImpersonateAsSystem(),
_ => Mesh.ServiceProvider.GetRequiredService<IMeshService>()
.Query<MeshNode>(MeshQueryRequest.FromQuery($"path:{SelfUpdateHandover.InboxTarget}/{WebhookInbox.InboxContainer} scope:children")).Take(1))
.FirstAsync().Timeout(Budget).Await(TestContext.Current.CancellationToken);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 99fa68b. The test now uses Access.RunAsSystem(() => …).

Comment on lines +111 to +115
Observable.Using(
() => Access.ImpersonateAsSystem(),
_ => MeshService.Query<MeshNode>(MeshQueryRequest.FromQuery($"path:{InstanceSecrets.PathOf(key)}")).Take(1)
.Select(c => c.Items.FirstOrDefault()))
.FirstAsync().Timeout(Budget).Await(TestContext.Current.CancellationToken);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 99fa68b. Both helpers now use Access.RunAsSystem(() => …).

Comment on lines +128 to +132
() => Access.ImpersonateAsSystem(),
_ => MeshService.CreateOrUpdateNode(new MeshNode(Target)
{
Name = Target,
NodeType = "Markdown",

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 99fa68b. This one now uses Access.RunAsSystem(() => …).

Comment on lines +139 to +143
() => Access.ImpersonateAsSystem(),
_ => MeshService.Query<MeshNode>(MeshQueryRequest.FromQuery(
$"path:{Target}/{WebhookInbox.InboxContainer} scope:children")).Take(1))
.Select(c => c.Items.Count(n => n.NodeType == WebhookInbox.NodeType))
.FirstAsync().Timeout(Budget).Await(TestContext.Current.CancellationToken);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 99fa68b. This one now uses Access.RunAsSystem(() => …).

rbuergi and others added 2 commits September 27, 2026 09:56
…eds no vault act or mount

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…-announcement-key

# Conflicts:
#	memex/Memex.Portal.Shared/MemexConfiguration.cs
#	src/MeshWeaver.Documentation/Data/Architecture.md
@github-actions

github-actions Bot commented Sep 27, 2026 •

Copy link
Copy Markdown
Contributor

Test Results (shard 1)

1 690 tests  ±0   1 690 ✅ ±0   4m 20s ⏱️ +11s
    2 suites ±0       0 💤 ±0 
    2 files   ±0       0 ❌ ±0 

Results for commit f51a551. ± Comparison against base commit 2bb14d8.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Sep 27, 2026 •

Copy link
Copy Markdown
Contributor

Test Results (shard 4)

    3 files  ± 0      3 suites  ±0   6m 37s ⏱️ +15s
2 204 tests +28  2 204 ✅ +28  0 💤 ±0  0 ❌ ±0 
2 205 runs  +28  2 205 ✅ +28  0 💤 ±0  0 ❌ ±0 

Results for commit f51a551. ± Comparison against base commit 2bb14d8.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Sep 27, 2026 •

Copy link
Copy Markdown
Contributor

Test Results (shard 2)

762 tests  ±0   571 ✅ ±0   7m 8s ⏱️ -16s
  3 suites ±0   191 💤 ±0 
  3 files   ±0     0 ❌ ±0 

Results for commit f51a551. ± Comparison against base commit 2bb14d8.

♻️ This comment has been updated with latest results.

…fix refused, fail closed on a dead feed, catalog-ready gates, keyed texts

- InstanceSecretWriteGuard refuses non-system create/update/delete of InstanceSecret nodes;
  the catalog uses only enc:-tagged values.
- Node id is lower-cased; keys containing '--' are not admitted (no collisions).
- A value starting with 'enc:' is refused (the protector would store it verbatim).
- Rotation overlap clamped to 14 days; the enumeration surface is removed.
- Catalog subscribes via RunAsSystem; a faulted feed fails CLOSED (no per-sender keys).
- Writes and the self-updater's Announce/Test wait for the catalog's first listing.
- Every announcement outcome is recorded, transport failures included.
- Refusals, last-use results, test verdicts and sources render in the viewer's language.
- Password buffer cleared on every termination of the read.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Sep 27, 2026 •

Copy link
Copy Markdown
Contributor

Test Results (shard 5)

    5 files  ±0      5 suites  ±0   14m 49s ⏱️ +46s
4 266 tests +8  4 264 ✅ +8  2 💤 ±0  0 ❌ ±0 
4 270 runs  +8  4 268 ✅ +8  2 💤 ±0  0 ❌ ±0 

Results for commit f51a551. ± Comparison against base commit 2bb14d8.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Sep 27, 2026 •

Copy link
Copy Markdown
Contributor

Test Results

   17 files  ± 0     17 suites  ±0   36m 37s ⏱️ +29s
9 730 tests +36  9 537 ✅ +36  193 💤 ±0  0 ❌ ±0 
9 739 runs  +36  9 546 ✅ +36  193 💤 ±0  0 ❌ ±0 

Results for commit f51a551. ± Comparison against base commit 2bb14d8.

♻️ This comment has been updated with latest results.

@meshweaver-cloud
meshweaver-cloud Bot added this pull request to the merge queue Sep 27, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to a conflict with the base branch Sep 27, 2026
@meshweaver-cloud

Copy link
Copy Markdown
Contributor

ℹ️ Merge-queue steward: no action — removed from the queue with reason MERGE_CONFLICT — the steward takes no action for this reason.

…-announcement-key

# Conflicts:
#	src/MeshWeaver.Documentation/Data/Architecture/SelfUpdateAnnouncementKey.md
@meshweaver-cloud
meshweaver-cloud Bot added this pull request to the merge queue Sep 27, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Sep 27, 2026
@meshweaver-cloud

Copy link
Copy Markdown
Contributor

⛔ Merge-queue steward: left dequeued — a job other than a test shard failed — a build or gate failure is never a flake.
Group build: https://github.com/Systemorph/MeshWeaver/actions/runs/36311194922

  • failed job: Dependent suites (MeshWeaver.Plugins)

Not a catalogued flake. Fix the failure, or — with run URLs, an issue and an assertion-message pattern — add it to .github/known-flakes.json (see Doc/Architecture/MergeQueue). Re-queue with gh pr merge <n> --auto once the head is green; label queue-rejected marks this PR as needing a person.

@meshweaver-cloud meshweaver-cloud Bot added the queue-rejected The merge queue rejected this PR on an uncatalogued failure; a person owns it now label Sep 27, 2026
rbuergi added a commit that referenced this pull request Sep 27, 2026
…ward re-queues it, capped

Five core PRs (#5789 #5791 #5792 #5793 #5795) sat queue-rejected on 2026-09-27 for one reason:
'MeshWeaver.Plugins did not answer within 42 min (no verdict yet)' — the dependent's legs never got
a runner, while every candidate that did run was green. The steward classed that as a gate/build
failure ('never a flake') and left them out.

- await-dependent-verdict.py: silence exits EXIT_NO_VERDICT=3 (still red, never a pass); every
  verdict-shaped red stays exit 1.
- dotnet-test.yml: the wait step hands exit 3 to its own failing step, 'No verdict in time: the
  dependent's suites did not report (infrastructure)'.
- merge-queue-steward.py: a Dependent-suites job that failed ONLY on that step is starved, not
  failed — re-queue kind=infra, capped 2 per head sha with the marker comment; beside a build
  failure or an uncatalogued assertion it still rejects; a verdict red still rejects. Nine new
  self-test rows (66 ok).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@rbuergi
rbuergi added this pull request to the merge queue Sep 27, 2026
@rbuergi
rbuergi enabled auto-merge September 27, 2026 16:46
@rbuergi
rbuergi merged commit 915549b into main Sep 27, 2026
46 checks passed
rbuergi added a commit that referenced this pull request Sep 27, 2026
…pp tab too

The merge of main unioned the localization keys but left main's new
admin-only "Control lane" seed on the global settings page. The merged seed
test lists ControlLane among the admin tab ids (main) and requires every admin
tab to be an Admin-app tab (this branch), so PlatformSettingsTabSeedTest failed
3 of 6 at bac07bd. Move it like the other Administration seeds:
NodeSettings context, gated AdminOnly + NodeTypes [AdminApp], and listed in
AdminAppTabIds so the old /GlobalSettings/ControlLane link redirects to
/Admin/Settings/ControlLane. AdminApp.md's inventory lists it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@rbuergi
rbuergi deleted the feat/instance-secrets-announcement-key branch October 10, 2026 13:10
@rbuergi rbuergi changed the title feat(secrets): write-only secret entry in the portal — InstanceSecrets, the secret control, and pearl's announcement key without a vault act feat(secrets): write-only secret entry in the portal — InstanceSecrets, the secret control, and client-b's announcement key without a vault act Oct 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

queue-rejected The merge queue rejected this PR on an uncatalogued failure; a person owns it now

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants