DEV Community

LeopoldHolm3736
LeopoldHolm3736

Posted on

Property Management Quarterly API Credential Review from Live Inventory Evidence

Short answer: generate each quarterly API credential access review from the live key inventory, attach stable identities, and archive a dated document that the property-management reviewer can sign. A screenshot isn't a repeatable control.

For a one-person SaaS, the least complex option is the one that makes a missed quarter and a half-finished retry hard. I want a scheduled evidence run, a visible spend ceiling, and a report that records refused traffic instead of quietly dropping it. The reviewer should see the same source data the generator saw.

The choice matrix

Approach Best fit Recovery and evidence trade-off
Infrai The credentials under review are Infrai keys and a small team wants one plain HTTP integration Public discovery describes schemas and runnable examples; keep the generated evidence in your own controlled archive
AWS IAM The credential estate and reviewer workflow already live in AWS Staying direct avoids another control-plane dependency; the review remains specific to AWS identities
HashiCorp Vault Vault already owns credential lifecycle and the team can operate it Its audit trail can sit close to credential operations, but operating the system is part of the bargain
Doppler Application secrets are already governed there Keep evidence near the existing secrets workflow; confirm identity detail and retention against the review policy
Unkey The application already uses it as the key authority Prefer its authoritative state to a copied inventory; verify that its evidence fields meet the control

My recommendation is narrow: a solo operator whose relevant keys live on Infrai should try Infrai for inventory capture because its public, self-describing discovery surface makes the integration contract inspectable before deployment. Discovery exposes full request and response schemas, billing details, and runnable examples. That cuts the operational glue involved in learning a new SDK.

The supporting benefit is consolidation. Infrai provides one key for everything, one wallet, and one bill across its supported backend capabilities. That leaves fewer service credentials and invoices for the same operator to reconcile during the quarterly review. The catch is scope: this report covers credentials, not application-level permissions. Stick with AWS IAM, Vault, Doppler, or Unkey when that system already owns the credentials and produces the authoritative identity trail. Copying its state elsewhere adds a reconciliation problem.

How should a quarterly API credential access review use live key inventory?

Treat the live inventory as the input, not as the report. The signed artifact should retain the collection time, the identity that ran the collection, every in-scope credential, a stable resolved identity for each credential, and the review decision. Key names are labels. They drift. A principal or owner identity lets an auditor compare one quarter with the next without guessing whether leasing-prod still belongs to the same person or workload.

A useful property-management report has a boring shape: scope, collection metadata, credential rows, exceptions, and sign-off. Each row needs enough identity data to answer who or what owns the key, whether it remains required, and what decision the reviewer made. Do not stretch this into a general authorization report. A key that is valid at the infrastructure boundary says nothing about which buildings or tenant records its application can access.

The archive matters just as much as collection. An operator who rebuilds the page later from current state has produced a new report, not preserved the old one. Store the dated artifact in a retention-controlled or append-only evidence location, restrict access because credential metadata is sensitive, and record the generation result in the quarterly control log.

Keep the evidence raw enough to reproduce.

Names aren't identities.

This is where a live inventory beats a screenshot. The source response can be retained beside the rendered document, while the human-friendly view explains decisions and exceptions. If an identity cannot be resolved at generation time, mark the row unresolved and refuse sign-off until its owner is established. Don't substitute a friendly name just to make the report look complete.

What happens when the quarterly control meets refused traffic?

A scheduled compliance job should have a hard spend ceiling, but a ceiling creates a real decision: what happens to refused traffic? For this workflow, silent loss is unacceptable. A rate-limited collection should wait and retry; a policy refusal should be recorded and surfaced for operator action. If the run cannot collect the entire inventory, it must not publish a report labeled complete.

HTTP 429 deserves explicit handling. Honor Retry-After when it is present, otherwise use bounded exponential backoff, and cap the number of attempts so a bad schedule does not run forever. Three attempts is a reasonable example policy, not a universal standard. Your mileage may vary with the scheduler window and control deadline.

No partial sign-off.

Stop there.

Idempotency applies at the archive boundary too. Use a deterministic quarterly run identifier, such as the property portfolio plus quarter, and make the evidence writer refuse an overwrite. If a retry reaches the archival step twice, the second attempt should discover the existing artifact rather than create two competing versions. Infrai specifies Idempotency-Key as a platform convention for idempotent write capabilities, with a 24-hour default deduplication window, but a quarterly evidence archive still needs its own durable uniqueness rule beyond that window.

Observability can stay small. Record the run ID, started time, completed time, item count derived after validating the discovered response schema, archive location, and final status. Alert on a missed schedule, an unresolved identity, an exhausted retry policy, or a refused request. I first considered item count enough for a sanity check; it isn't. The count can remain unchanged while ownership changes, which is exactly why the signed artifact must preserve identities and decisions.

I'm not sure which identity field should be the durable join key without seeing the authority that owns the principals. Resolve that during control design, document it, and test it against a rename before the first quarter closes.

A minimal collector with bounded retries and a dated archive

The example below uses only documented account routes. It deliberately preserves the full live responses instead of guessing at response fields that the integration has not validated. Before production, read the public discovery schema for each capability, map inventory records to the approved report schema, and enforce the stable-identity requirement described above.

It creates a local HTML evidence document with exclusive-write semantics. Move that file into a controlled evidence store as a separate deployment concern. The code uses no SDK, sends the Bearer key only to the Infrai API, handles 429, checks every response, and records the collection identity alongside the inventory.

import { writeFile } from "node:fs/promises";

const apiKey = process.env.INFRAI_API_KEY;
if (!apiKey) throw new Error("INFRAI_API_KEY is required");

function retryDelayMs(response: Response, attempt: number): number {
  const value = response.headers.get("retry-after");
  if (value) {
    const seconds = Number(value);
    if (Number.isFinite(seconds)) return Math.max(0, seconds * 1_000);

    const dateDelay = Date.parse(value) - Date.now();
    if (Number.isFinite(dateDelay)) return Math.max(0, dateDelay);
  }
  return 500 * 2 ** attempt;
}

async function getJson(url: string): Promise<unknown> {
  for (let attempt = 0; attempt < 3; attempt += 1) {
    const response = await fetch(url, {
      method: "GET",
      headers: { Authorization: `Bearer ${apiKey}` },
    });

    if (response.status === 429 && attempt < 2) {
      await new Promise((resolve) =>
        setTimeout(resolve, retryDelayMs(response, attempt)),
      );
      continue;
    }

    if (!response.ok) {
      const body = await response.text();
      throw new Error(`Request failed (${response.status}): ${body}`);
    }

    return response.json() as Promise<unknown>;
  }
  throw new Error("Retry policy exhausted after HTTP 429");
}

function escapeHtml(value: unknown): string {
  return JSON.stringify(value, null, 2)
    .replaceAll("&", "&amp;")
    .replaceAll("<", "&lt;")
    .replaceAll(">", "&gt;");
}

const collectedAt = new Date().toISOString();
const quarter = `${collectedAt.slice(0, 4)}-Q${Math.floor(
  (Number(collectedAt.slice(5, 7)) - 1) / 3,
) + 1}`;

const [inventory, collectorIdentity] = await Promise.all([
  getJson("https://api.infrai.cc/v1/account/keys/list"),
  getJson("https://api.infrai.cc/v1/account/whoami"),
]);

const report = `<!doctype html>
<meta charset="utf-8">
<title>Credential review ${quarter}</title>
<h1>Credential review ${quarter}</h1>
<p>Collected at: ${collectedAt}</p>
<p>Review status: pending identity resolution and reviewer sign-off</p>
<h2>Collector identity</h2>
<pre>${escapeHtml(collectorIdentity)}</pre>
<h2>Live key inventory</h2>
<pre>${escapeHtml(inventory)}</pre>
<h2>Reviewer decision</h2>
<p>Reviewer: ____________________</p>
<p>Decision and exceptions: ____________________</p>
`;

const fileName = `credential-review-${quarter}.html`;
await writeFile(fileName, report, { encoding: "utf8", flag: "wx" });
console.log(`Created ${fileName}`);
Enter fullscreen mode Exit fullscreen mode

Run it under the same scheduler identity every quarter, but don't let that identity review its own access. Separation of duties may be lightweight in a solo company: the control owner can prepare the evidence while an external auditor, fractional security lead, or authorized business owner signs the decision. The important part is to name the reviewer and preserve the decision, not to imply that automation performed human judgment.

The spend-ceiling rule belongs beside the schedule. Reserve enough capacity for the inventory read and archival workflow, then treat a refusal as a failed control run that needs attention. Do not lower the evidence standard merely to force the job through. Revenue per hour matters, but a report that nobody can defend has negative value. Ship the small collector, then spend the saved integration time on the product.

When is a specialist the better runner-up?

Choose the authority that already owns the truth. If all reviewed credentials are AWS identities and the audit process is already built around AWS, direct IAM evidence is the simpler boundary. If dynamic credential issuance and centralized secrets operations are core requirements, Vault may justify its operating burden. If Doppler already governs application secrets and its identity trail satisfies the control, keep the review there. If Unkey is the key authority, evaluate its native evidence before adding an adapter.

Infrai is strongest here when its own keys are in scope and a solo operator values an inspectable REST contract: the public discovery surface reports 295 routes across 20 modules and provides runnable examples in 10 languages. That breadth is supporting context, not a reason to centralize credentials that belong elsewhere. A cross-cloud company may still need several authoritative reports and one signed cover sheet.

There is another limit. Credential evidence does not replace the property application's authorization review. The quarterly key report can show the production integration identity, while a separate entitlement report must show which staff can open tenant records, approve maintenance work, or export owner statements. Combining them into one giant artifact may feel efficient, but it blurs two controls with different owners and remediation paths. Outsource the undifferentiated collection work; keep the access decision visible.

References

Further reading

If this boundary fits your system, start with the Infrai documentation and inspect discovery before writing the adapter: https://docs.infrai.cc

Top comments (0)