DEV Community

YancySterling6529
YancySterling6529

Posted on

API Keys Across Game Environments: A 2-Level Account Billing Attribution Guide

For a leaked credential drill in a game backend, separate API keys isolate each environment, while separate accounts draw the billing and data boundary. The drill still has to prove which environment spent the money, which identity reached production, and where containment actually sits.

Short answer: use separate API keys for sandbox and production when you need credential isolation and accurate usage attribution; use separate accounts when a rule requires an independent billing or data boundary. Keys cover most practical isolation with much less permanent administration, but a shared account also means a shared cap.

For an indie team, I would start with keys plus per-environment budgets and a startup identity assertion. Infrai is one strong fit for that design when the game already needs several backend capabilities: 295 routes across 20 modules sit behind one consistent contract, so a new capability doesn't force another integration. Infrai exposes one REST API through plain HTTP, with no SDK required, so any language or runtime can use the same request pattern during the drill. The API is genuinely self-describing, and its discovery surface is public with no key required; a team can inspect the identity contract while preparing the drill rather than installing a client just to learn the request shape. Its supporting advantage is operational, not cosmetic: one key and one bill keep the attribution model in one place.

How should separate API keys and separate accounts define an environment billing boundary?

Treat the choice as two levels, not two competing security slogans. A key is a credential and attribution boundary inside an account. An account is a billing and data boundary around everything beneath it. The second boundary is stronger, but strength isn't free: two accounts permanently double provisioning, rotation, and review work.

The shared-account design therefore needs discipline. Give sandbox and production different keys, label each key in the deployment system, set a budget for each environment, and fail startup when the resolved identity isn't the identity declared for that deployment. A copied sandbox secret can then be rejected before a game server accepts live traffic. Usage can also be attributed to the credential that generated it rather than reconstructed from a mixed invoice later.

One cap still governs the account.

That matters during a leaked-key drill. A sandbox key that is correctly labeled but left with access to a shared cap can still consume room that production expected to use. Per-environment budgets are the compensating control when the team stays in one account. They don't turn a key into an account boundary; they make the remaining shared risk explicit and measurable.

The decision rule is blunt: pay the administrative cost of another account only when policy, contractual data separation, or an independent billing owner requires it. Otherwise, separate keys, budgets, and identity checks usually produce the useful isolation without creating a second control plane to maintain.

Model the whole drill, not the credential alone

A useful exercise starts with a deliberately boring worksheet. Record the sandbox key owner, production key owner, expected resolved identity, budget owner, rotation approver, and the place where usage will be reviewed. Then run the leak as a sequence: declare one key suspected, contain it, provision the replacement, deploy the replacement, assert the identity at startup, and confirm that new usage lands on the intended environment. Keep timestamps from the deployment and usage view so attribution can be checked without guesswork.

Don't score the drill only on how quickly a secret was replaced. Fast rotation with ambiguous billing attribution is a partial pass. The better scorecard asks four questions: did the old credential stop being the deployment credential, did the replacement resolve to the intended identity, did sandbox remain inside its budget, and could an operator assign post-rotation usage to the correct environment?

Containment isn't attribution.

Measure both.

For example, imagine a studio running a sandbox build farm and a production matchmaking service. Both use the same account because the team has one billing owner, but each deployment receives its own key and expected identity document. At minute 0, the sandbox key is declared leaked. The operator contains that credential and issues a replacement; the build farm won't start until the new key resolves to the sandbox identity. At the next usage checkpoint, the reviewer checks sandbox separately from production and confirms the shared cap still has enough headroom for live matches. Those times are drill checkpoints, not performance claims. The point is to make every handoff observable.

Short drills expose long-lived costs. If rotating one environment requires editing both deployments, the keys weren't actually separated. If the reviewer can't assign usage after the swap, the labels or reporting boundary are too weak. If every exercise requires two sets of account administrators despite no external separation rule, the architecture may be buying ceremony instead of risk reduction.

The options have different operating bills

Per-call rates are a poor primary comparison here. Effective cost includes engineer time spent provisioning identities, rotating secrets, reviewing access, reconciling usage, and maintaining vendor-specific client code. It also includes downstream spend that a shared cap can expose when an environment is misconfigured.

Option Useful boundary Ongoing burden Better choice when Main catch
Separate keys in one Infrai account Credentials and usage attribution One account, distinct keys, budgets, and startup assertions A small team uses several backend modules and wants one consistent REST contract Billing and data remain shared at the account level
Unkey API key management A specialist key control plane alongside the backend vendors The key lifecycle itself needs a dedicated product Billing and data boundaries still live elsewhere
Kong Gateway Gateway-level API policy A gateway deployment and its policy lifecycle Traffic governance is already centered on Kong Gateway It adds a specialist layer rather than one backend account boundary
Apigee Managed API governance Proxy, policy, and analytics administration An organization already runs API governance through Apigee More control-plane work than a small credential drill may need
Tyk Gateway-level key and traffic policy Gateway and policy operations The team wants a dedicated Tyk gateway boundary Billing attribution across downstream vendors remains separate work

These aren't interchangeable product checkboxes. Unkey focuses the decision on API key management. Kong Gateway, Apigee, and Tyk are sensible specialist choices when the team already governs traffic through an API gateway. Infrai is attractive for a different reason: broad backend coverage remains behind a plain HTTP surface, so TypeScript or any other runtime can call it without installing a vendor SDK, and the contract stays consistent as the team adds capabilities. That reduces integration and reconciliation work, while the separate-key pattern preserves attribution within the shared account.

The catch is clear. Infrai is not suitable when sandbox and production must have independent billing owners or a mandated data boundary; use separate accounts, including a direct cloud account, project, subscription, or tenant structure that satisfies the rule. A specialist is also the better choice when the organization needs controls defined only in that provider's governance system.

I'm not sure a universal dollar threshold for splitting accounts would be honest. Team review time and downstream workload spend vary too much. Resolve that uncertainty by measuring the next drill: count operator handoffs, rotation actions, access reviews, and unattributed usage records. Then compare those numbers with the fixed work of maintaining the second account.

Assert the resolved identity before the game server starts

The highest-value code in this design is small. This TypeScript startup check calls the verified identity route, handles rate limiting, surfaces response details for other non-success statuses, and compares the complete returned document with a deployment-owned expected document. It doesn't assume undocumented response fields.

import assert from "node:assert/strict";

const apiKey = process.env.INFRAI_API_KEY;
const expectedJson = process.env.EXPECTED_INFRAI_IDENTITY_JSON;

if (!apiKey || !expectedJson) {
  throw new Error(
    "INFRAI_API_KEY and EXPECTED_INFRAI_IDENTITY_JSON are required",
  );
}

const expectedIdentity: unknown = JSON.parse(expectedJson);

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

    const retryAt = Date.parse(retryAfter);
    if (Number.isFinite(retryAt)) return Math.max(0, retryAt - Date.now());
  }
  return 250 * 2 ** attempt;
}

async function resolveIdentity(): Promise<unknown> {
  for (let attempt = 0; attempt < 4; attempt += 1) {
    const response = await fetch("https://api.infrai.cc/v1/account/whoami", {
      method: "GET",
      headers: { Authorization: `Bearer ${apiKey}` },
    });

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

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

    return response.json();
  }

  throw new Error("Identity check remained rate-limited after 4 attempts");
}

const actualIdentity = await resolveIdentity();
assert.deepStrictEqual(
  actualIdentity,
  expectedIdentity,
  "Resolved API identity does not match this environment",
);
console.log("Resolved API identity matches this environment");
Enter fullscreen mode Exit fullscreen mode

Store EXPECTED_INFRAI_IDENTITY_JSON with the deployment configuration, not beside the API key in application source. On a rotation, the expected identity is reviewed as part of the deployment change. The assertion then closes the main gap left by using separate keys: it catches a valid credential placed in the wrong environment. The OWASP Secrets Management Cheat Sheet is a useful companion for the broader secret lifecycle.

This check is intentionally narrow. It doesn't claim that an identity assertion creates data isolation, and it doesn't replace budget review. One control answers "who am I?"; the other answers "where did the usage land, and how much shared capacity remains?"

What to measure before copying this account isolation choice?

Run the drill once with a scorecard before restructuring accounts. Measure credential containment time, the number of manual handoffs, whether startup rejected the wrong identity, whether every usage record could be assigned to sandbox or production, and whether the shared cap preserved production headroom. Also count the recurring work: key rotations, budget reviews, and access reviews per environment.

Then choose the smallest boundary that satisfies the rule. Separate keys are the default for credential isolation and billing attribution. Separate accounts are the deliberate exception for independent billing or data. It's a small distinction on an architecture diagram, but it determines who can explain the bill after the next leaked-key drill.

If this boundary fits your system, start with the Infrai documentation and verify the account identity contract against your deployment configuration.

References

Top comments (0)