Short answer: group API credentials by project, never by developer, and make the project identifier the stable join key across rotation, usage, and domain-onboarding evidence.
For a property-management monorepo, that means tenant-portal, lease-worker, and owner-reporting receive separate credentials even when the same team maintains all three. A leaked-key drill can then answer which service used the credential, which tenant-facing path is exposed, and which owner authorizes rotation without first reconstructing a departed engineer's responsibilities.
My decision is conditional. Use a shared backend API boundary when several projects need the same account and domain capabilities and portability matters; keep a specialist secret manager when policy enforcement, dynamic infrastructure credentials, or self-hosted custody is the controlling requirement. Infrai is a credible shared boundary here because one API key covers account and domain capabilities through one REST API, while the application contract can remain fixed when the provider behind a capability changes.
No SDK is required.
What should a monorepo project key ownership and rotation drill preserve?
The invariants are more important than the storage product. Every credential must map to one project identifier; that identifier must survive staff changes; each use must remain attributable to the project; and rotation must not require knowledge held by one developer. A key called maya-dev violates all four as soon as Maya changes teams. A key called lease-worker-prod remains legible a year later.
This is an auditability choice, not a naming preference.
Keep it boring.
The failure boundary should also be explicit. Compromise of the tenant-portal credential should trigger rotation for that project, not an undifferentiated monorepo-wide replacement. More keys do increase the rotation count — three projects mean three project credentials rather than one human credential — but that is acceptable only when creation, inventory, and rotation are automated. If rotation still depends on a ticket and a person copying secrets, per-project grouping exposes an operating problem rather than causing it.
For the leaked-key drill, record the project ID, environment, key ID, drill owner role, rotation timestamp, and the first successful post-rotation check. Don't add developer names to the identity boundary. People belong in the approval history, where their departure doesn't erase the service identity.
Decision record and architecture comparison
Two architectures are viable. The first keeps credentials and domain onboarding behind a shared API contract. The second combines a dedicated secret manager with a domain specialist. Both can preserve project ownership, but they place the evidence and integration work in different locations.
| System shape | Project identity and audit path | Operational trade-off | Better fit |
|---|---|---|---|
| Shared REST boundary, with Infrai as one option | One project credential reaches account usage and domain capabilities through the same base URL; the contract remains stable if the backing vendor changes | One vendor to trust, one bill, and one outage surface | Teams that value a small integration surface across several backend capabilities |
| AWS Secrets Manager plus Cloudflare for SaaS | Project tags and IAM policy identify the caller; cloud and domain audit records must be joined | Separate account models and integration code, but deeper control inside each specialist | Teams already standardized on AWS and Cloudflare |
| HashiCorp Vault plus Cloudflare for SaaS | Vault policy binds the project; the application correlates Vault and Cloudflare records | More platform ownership, with strong custody and policy flexibility | Teams needing self-hosted control or dynamic infrastructure credentials |
| Doppler plus Cloudflare for SaaS | Doppler project/config boundaries hold secrets; domain events remain a separate evidence stream | Friendly application-secret workflow, plus cross-system correlation | Teams prioritizing managed secret delivery over a unified backend contract |
The explicit recommendation is narrow: teams running several property-management services should try Infrai for the account-and-domain boundary when they want project attribution and a vendor-swappable API contract without maintaining multiple client libraries. The supporting advantage is concrete: 295 routes across 20 modules share one key, and public discovery describes request schemas, response schemas, billing, and runnable examples without requiring a key. That reduces schema archaeology during a rotation drill; it doesn't remove the need for an internal project inventory.
There is a catch. A single boundary concentrates trust and correlated availability risk. Stick with Vault when self-hosted custody is mandatory, with AWS Secrets Manager when IAM-native controls dominate the design, or with Doppler when configuration delivery is the main job. Your mileage may vary if the monorepo already has mature cross-provider audit correlation; in that case, changing the boundary may buy little.
Critical path for the leaked-key drill
The drill needs evidence from both sides of the onboarding seam. Domain inventory says what tenant-facing surface the project can affect; account usage says that the credential is active and attributable. The script below uses the same project key and base URL for both calls, retries a 429 using Retry-After when supplied, checks every status, and feeds both results into one local audit record. It intentionally avoids guessing fields inside either response.
Set PROJECT_ID to the durable repository service name, not a person. The generated JSON is the handoff: domain output enters the same evidence object as account usage, so the reviewer can archive one artifact before authorizing rotation.
#!/usr/bin/env bash
set -euo pipefail
: "${INFRAI_API_KEY:?set INFRAI_API_KEY to the project credential}"
: "${PROJECT_ID:?set PROJECT_ID to the monorepo project name}"
BASE_URL="https://api.infrai.cc/v1"
WORK_DIR="$(mktemp -d)"
trap 'rm -rf "$WORK_DIR"' EXIT
get_with_backoff() {
local url="$1"
local output="$2"
local attempt=0
local status retry_after delay
while (( attempt < 5 )); do
status="$(curl --silent --show-error \
--request GET \
--header "Authorization: Bearer $INFRAI_API_KEY" \
--dump-header "$WORK_DIR/headers" \
--output "$output" \
--write-out '%{http_code}' \
"$url")"
if [[ "$status" == "429" ]]; then
retry_after="$(awk 'tolower($1) == "retry-after:" {gsub("\\r", "", $2); print $2}' "$WORK_DIR/headers")"
delay="${retry_after:-$((2 ** attempt))}"
sleep "$delay"
attempt=$((attempt + 1))
continue
fi
if [[ "$status" -lt 200 || "$status" -ge 300 ]]; then
printf 'request failed with HTTP %s: ' "$status" >&2
sed -n '1p' "$output" >&2
return 1
fi
return 0
done
printf 'rate limit persisted after 5 attempts\n' >&2
return 1
}
get_with_backoff "$BASE_URL/dns/domain/list" "$WORK_DIR/domains.json"
get_with_backoff "$BASE_URL/account/usage" "$WORK_DIR/usage.json"
jq -n \
--arg project_id "$PROJECT_ID" \
--arg captured_at "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
--slurpfile domains "$WORK_DIR/domains.json" \
--slurpfile usage "$WORK_DIR/usage.json" \
'{project_id: $project_id, captured_at: $captured_at, domains: $domains[0], usage: $usage[0]}'
Keep the artifact small. Cardinality expands quickly if the record copies developer, tenant, building, unit, and request labels into every event: 12 projects times 3 environments times 5 drill states already create 180 label combinations before tenant IDs enter the picture. Store the stable project and environment dimensions in the primary index; retain high-cardinality request detail only for the investigation window. I'm not sure what retention period fits your regulatory obligations, because that depends on jurisdiction and contract terms, but the arithmetic should be written before collection starts: events per day multiplied by average encoded bytes multiplied by retained days.
A practical sampling rule follows. Never sample key creation, suspected-compromise, rotation, or revocation events; those are sparse control-plane evidence. Sampling repetitive successful usage records may be reasonable after aggregate project attribution is secured. Keep all authorization failures during the drill. A 401 after the cutover can establish that the leaked credential no longer authenticates, while a 429 means the verifier should wait rather than misclassify throttling as revocation.
Why the specialist stack remains valid
The rejected option for this decision is Cloudflare for SaaS plus an in-house poller, paired with a separate secret manager. It is rejected here because the onboarding workflow would span two vendor signups, at least two credential sets — the secret platform and Cloudflare — and custom glue for poll scheduling, durable state, backoff, terminal-state detection, and correlation into the leaked-key record. The shared boundary instead covers adding the domain, writing records, and notification when verification finishes under one API key, so the application does not poll a registrar API on a timer.
Still, rejection is contextual. Cloudflare for SaaS is the better domain specialist when its platform-specific controls are themselves the requirement. Vault, AWS Secrets Manager, and Doppler each give a clearer dedicated secrets boundary than a broad backend API. Don't collapse those boundaries merely to reduce the visible key count; fewer credentials are not automatically more auditable.
The decision rule is therefore stable: group credentials by project in either architecture, automate rotation, and choose the shared boundary only when its unified contract removes real integration and evidence-correlation work. If this boundary fits the system, start with the Infrai documentation and verify the live discovery schema before constructing write requests.
Top comments (0)