feat(miner): wire AMS's hosted entry point to the #8202 tenant-secret bootstrap#8263
Merged
Conversation
… bootstrap (#8246) Adds tenant-credential-resolution.ts, a duplicated (not imported -- cross- package import from packages/loopover-miner into root src/ fails tsc with TS6059, confirmed) exchange of LOOPOVER_TENANT_SECRET_TOKEN against the broker, mirroring src/orb/broker-client.ts's fetchBrokeredStoredSecret for ORB. hosted-entry.ts resolves it once per wake, best-effort -- no code in this package consumes the resolved value yet (the miner's stores are unconditionally local SQLite), so this proves the mechanism is wired for AMS without making a scheduled cycle fragile against an unused value, mirroring #8202/#8253's own "prove it works, defer consumption" precedent for ORB.
Contributor
|
Superagent didn't find any vulnerabilities or security issues in this PR. |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #8263 +/- ##
==========================================
- Coverage 92.14% 91.62% -0.52%
==========================================
Files 785 786 +1
Lines 78715 78749 +34
Branches 23759 23773 +14
==========================================
- Hits 72531 72154 -377
- Misses 5062 5524 +462
+ Partials 1122 1071 -51
Flags with carried forward coverage won't be shown. Click here to find out more.
|
This was referenced Jul 23, 2026
JSONbored
added a commit
that referenced
this pull request
Jul 23, 2026
…th "secret" (#8266) check-miner-package.ts's FORBIDDEN_PATH filter matched the keyword "secret" (or "private...key") anywhere in a path, including inside legitimately-named .js/.ts source files -- caught while shipping PR #8263, whose tenant-secret-resolution.ts had to be renamed to tenant-credential-resolution.ts to work around it. Splits the filter: exact-name dotfiles (.env/.npmrc/.dev.vars) stay forbidden regardless of extension, and the keyword heuristic now only applies to non-source files. Source files are exempt because a real leaked secret VALUE is already caught unconditionally by the separate FORBIDDEN_CONTENT scan regardless of filename -- the keyword check was only ever a coarse, filename-based backstop for stray credential-shaped files like .pem/.json.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes #8246 -- the AMS half of #8202/#8253's hosted tenant-secret bootstrap mechanism.
OrbTenantContainergets that mechanism close to free because it runs the unmodified self-host image, which already importssrc/orb/broker-client.ts.AmsTenantContainer's image (packages/loopover-miner) had zero broker-aware code -- this PR adds it.Cross-package import isn't possible here (confirmed empirically, not assumed):
packages/loopover-miner/tsconfig.jsonscopes"rootDir": "."to the package itself, so a relative import reaching into rootsrc/orb/broker-client.tsresolves outsiderootDirand failstscwith TS6059. This matches an already-established convention in this codebase for the exact same package boundary --control-plane/src/secret-driver.ts's own header comment duplicates a constant "rather than imported" for an identical reason, andcontrol-plane/src/http-app.ts'sHOSTED_CYCLE_COMMANDScomment explicitly cross-references this file as the same case. Sopackages/loopover-miner/lib/tenant-credential-resolution.tsduplicates the exchange logic rather than importing it -- same URL-safety validation, samePOST /v1/orb/tokencall shape, same response parsing asfetchBrokeredStoredSecret.Scope check before writing this: I verified there is currently no consumer anywhere in
packages/loopover-minerfor a resolved database connection string -- the miner's own stores are unconditionally local SQLite (store-db-adapter.ts's own comment: AMS can "later" swap in a Postgres adapter, hasn't happened), and the existingattempt-db-fork-config.ts(#7858's Neon branch-per-attempt config) is a completely separate, unrelated concept (a disposable per-attempt fork of some other database, not the tenant's own persistent one). This exactly mirrors ORB's own situation -- #8202/#8253 shipped the mechanism and the exchange function but never wired a runtime consumer intosrc/server.tseither, deliberately deferring that. SoresolveTenantSecretis a best-effort wrapper (unlike the throwingfetchTenantSecret/ORB'sfetchBrokeredStoredSecret): a broker outage or unconfigured token must not block a scheduleddiscover/manage-poll/attemptcycle from running when nothing consumes the result yet.Naming note: the new file is
tenant-credential-resolution.ts, nottenant-secret-resolution.ts--scripts/check-miner-package.ts'sFORBIDDEN_PATHfilter has a coarse.*secret.*filename heuristic (aimed at stray files like.env/.pem, not descriptively-named source code) that the original name tripped. Renaming was more surgical than loosening a security-relevant filter, and "credential" already matches this codebase's own established term for the same concept (ORB_SECRET_TYPE_TENANT_DB_CREDENTIAL). The exported symbols (fetchTenantSecret,resolveTenantSecret,TenantSecret) keep "Secret" in their names, matchingbroker-client.ts's own naming for the function this duplicates.Where it's wired
hosted-entry.ts'srunHostedEntrycallsresolveTenantSecret(env)once per wake, right after the known-cycle-name check and before the health server starts, and logs the outcome (ams_hosted_entry_tenant_secret_resolved,resolved/secretType). Never blocks or changes cycle dispatch either way.Tests
test/unit/miner-tenant-credential-resolution.test.ts: mirrorstest/unit/orb-broker-client.test.ts's ownfetchBrokeredStoredSecretcoverage (success, custom broker URL, unsafe-URL rejection, localhost dev exception, both defensive??branches, non-OK response, missingsecretValue) plusresolveTenantSecret's best-effort behavior (no token -> null without calling fetch, blank token -> same, success passes through, exchange failure is swallowed + warn-logged + returns null, including a non-Error rejection).test/unit/miner-hosted-entry.test.ts: newdescribeblock provingresolveTenantSecretis called with the rightenv, never blocks dispatch on either outcome, the resolved/omitted log line is correct, and it's never called at all on the early-return (unknown cycle name) path.Verified: full unsharded
npm run test:coverage(21030/21051 passing, only pre-existing skips, 0 failures -- run twice, once before and once after a fix described below),tsc --noEmit --incremental falseclean,npm run build:minerclean,npm run test:miner-deployment-docs-auditclean (the new file never readsprocess.envdirectly, only via a passed-inenvparam, so it's outside that audit's static-analysis scope),git diff --checkclean, real c8-scoped coverage 100% line+branch on both touched/new files.Found and fixed along the way: my first pass named the file
tenant-secret-resolution.ts, which failedtest/unit/check-miner-package.test.ts > passes on the real miner workspace package--scripts/check-miner-package.ts's filename-based secret filter caught it. Renamed rather than touching the filter itself (see Naming note above); the full suite re-run clean afterward.Test plan
npm run test:coverage(full unsharded, 21030/21051 passing, 0 failures)tsc --noEmit --incremental falsenpm run build:minernpm run test:miner-deployment-docs-auditnpx tsx scripts/check-miner-package.ts(real pack dry-run)git diff --check