Skip to content

✨ server: add allower#839

Open
aguxez wants to merge 5 commits intomainfrom
gcp-allower
Open

✨ server: add allower#839
aguxez wants to merge 5 commits intomainfrom
gcp-allower

Conversation

@aguxez
Copy link
Contributor

@aguxez aguxez commented Feb 25, 2026

closes #643

Summary by CodeRabbit

  • New Features

    • GCP KMS integration for account keys, an account-allower (firewall) flow, and automated asset poking for ETH/WETH/ERC‑20
  • Bug Fixes

    • Simplified per-account activity orchestration, error handling, and poke/account activation flow
  • Tests

    • Added GCP credentials validation tests and expanded tests for account, firewall, and poke behaviors
  • Chores

    • Added runtime env vars and cloud/KMS-related dependencies

Open with Devin

Greptile Summary

This PR integrates Google Cloud KMS (via @google-cloud/kms and @valora/viem-account-hsm-gcp) to sign firewall allow transactions with an HSM-backed key, and adds a new keeper.poke utility that proactively pokes account assets (ETH, WETH, ERC-20) after KYC approval and on activity webhooks.

Key changes:

  • server/utils/gcp.ts — new module handles credential bootstrapping (triple base64 decode → /tmp/gcp-service-account.json), lazy initialization with promise caching, and retryable KMS error classification.
  • server/utils/accounts.ts — adds getAccount() (GCP KMS → viem LocalAccount), allower() (wallet client that wraps allow on the Firewall contract), and poke() (scans balances and pokes every non-zero asset via exaSend).
  • server/hooks/persona.ts — after KYC approval calls allow then fire-and-forgets poke; allowerPromise caches the expensive KMS init across requests.
  • server/hooks/activity.ts — calls poke after account deployment, with NotAllowed in the ignore list.

Verified findings:

  • The delay function in poke's withRetry uses bitwise left-shift which overflows at count >= 31 (currently safe with retryCount: 10, but a footgun if the parameter is raised).
  • The DECODING_ITERATIONS = 3 constant lacks documentation explaining the triple base64 encoding strategy.
  • The keeper client's onFetchRequest hook lacks error handling, unlike the allower version, making it susceptible to unhandled exceptions on malformed RPC payloads.

Confidence Score: 3/5

  • Mergeable with awareness of three concrete code-quality issues requiring follow-up.
  • The PR's core allow+poke flow is functional and well-tested. However, three verified findings need attention: (1) the bitwise-shift overflow in retry delay is a preventable footgun if retryCount is raised, (2) the triple base64 decoding strategy lacks documentation creating fragility, and (3) the missing try/catch in keeper's onFetchRequest creates an inconsistency with allower that could surface unhandled exceptions. None of these are critical under normal conditions, but they represent code-quality debt worth fixing before production ramp-up.
  • server/utils/accounts.ts (bitwise-shift delay, error handling inconsistency) and server/utils/gcp.ts (undocumented constant).

Last reviewed commit: 0275499

Context used:

  • Rule from dashboard - AGENTS.md (source)
  • Rule from dashboard - CLAUDE.md (source)

@changeset-bot
Copy link

changeset-bot bot commented Feb 25, 2026

🦋 Changeset detected

Latest commit: a4d043e

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@exactly/server Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai
Copy link

coderabbitai bot commented Feb 25, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds GCP KMS integration and runtime envs, replaces default keeper with a named keeper from server/utils/accounts (new getAccount/allower/withExaSend APIs), integrates firewall allower and keeper.poke into persona/activity hooks, restructures account orchestration, and updates tests/mocks.

Changes

Cohort / File(s) Summary
Changesets & small config
.changeset/lucky-jokes-change.md, .changeset/silly-yaks-divide.md, cspell.json
New changesets and minor cspell addition.
Deployment & runtime envs
.do/app.yaml, server/script/openapi.ts, server/vitest.config.mts
Add GCP env vars: GCP_BASE64_JSON, GCP_KMS_KEY_RING, GCP_KMS_KEY_VERSION, GCP_PROJECT_ID for runtime and tests.
Dependencies
server/package.json
Add @google-cloud/kms and @valora/viem-account-hsm-gcp.
GCP utilities & credential init
server/utils/gcp.ts, server/test/utils/gcp.test.ts
New GCP credential initializer, idempotent init/reset, hasCredentials, retryable-KMS-error helper, and tests for file write/access and permissions.
Accounts API & keeper export
server/utils/accounts.ts, server/test/mocks/accounts.ts, server/test/utils/accounts.test.ts
New keeper named export and APIs: getAccount(), allower(), withExaSend(); extender signatures adjusted; tests/mocks updated to import from .../accounts.
Removed legacy keeper
server/utils/keeper.ts
Removed previous default-exported keeper and legacy extender/exaSend implementation.
Hook imports & behavior
server/hooks/activity.ts, server/hooks/persona.ts, server/hooks/block.ts, server/hooks/panda.ts
Switch to named keeper from utils/accounts; activity refactored to account-lookup per-account spans and simplified flows; persona integrates firewall allower + keeper.poke and adjusts addCapita/account derivation.
Tests & mocks updates
server/test/hooks/*.test.ts, server/test/api/card.test.ts, server/test/e2e.ts, server/test/mocks/accounts.ts, server/test/hooks/persona.test.ts
Replace keeper imports/usages, adapt tests to keeper.poke semantics and balance setups, add accounts mocks and firewall/allower mocks, add GCP-related tests.
Vitest / test env
server/vitest.config.mts
Expose new GCP env vars to Vitest environment.

Sequence Diagram

sequenceDiagram
    participant Client
    participant PersonaHook as Persona Hook
    participant RiskSvc as Risk Assessment
    participant Allower as Firewall/Allower
    participant GCP as GCP KMS
    participant Chain as Chain / keeper

    Client->>PersonaHook: POST inquiry
    PersonaHook->>RiskSvc: perform risk assessment
    RiskSvc-->>PersonaHook: pass/fail

    alt pass
        PersonaHook->>PersonaHook: derive account
        PersonaHook->>Allower: getAllower()
        Allower->>GCP: init credentials / request KMS-backed account
        GCP-->>Allower: LocalAccount / credentials
        Allower-->>PersonaHook: allower client ready
        PersonaHook->>Allower: allow(account)
        Allower-->>PersonaHook: allow result
        PersonaHook->>Chain: keeper.poke(account, { ignore: [...] })
        Chain-->>PersonaHook: tx hash / result
        PersonaHook-->>Client: 200 success
    else fail
        PersonaHook-->>Client: rejection
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Suggested reviewers

  • cruzdanilo
  • nfmelendez
  • dieguezguille
🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title '✨ server: add allower' accurately reflects the main changes: adding an allower function to the server's accounts module for firewall integration, which is a central addition across multiple files.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch gcp-allower

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist
Copy link

Summary of Changes

Hello @aguxez, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the server's account management and security by integrating Google Cloud KMS for a new allower functionality. It centralizes wallet client operations into a dedicated utility, ensuring that account creation and asset 'poking' are handled securely and efficiently, especially after user verification. The changes also include necessary environment configurations and dependency updates to support this new cryptographic infrastructure.

Highlights

  • GCP KMS Integration for Allower: Introduced a new allower functionality that leverages Google Cloud Key Management Service (KMS) for secure cryptographic operations, specifically for allowing accounts within the system. This enhances the security posture of account management.
  • Refactored Account Management Utilities: Consolidated and refactored the keeper and new allower wallet client logic into a dedicated server/utils/accounts.ts module. This centralizes account-related transaction sending and interaction with smart contracts.
  • Automated Account Poking Post-KYC: Implemented logic within the persona hook to automatically 'poke' an account after a successful Know Your Customer (KYC) process. This ensures that newly verified accounts are properly initialized and their assets are recognized by the system, including interaction with the new allower firewall.
  • Environment Variable Configuration: Added new environment variables (GCP_KMS_KEY_RING, GCP_KMS_KEY_VERSION, GCP_PROJECT_ID, GCP_BASE64_JSON) to .do/app.yaml and server/vitest.config.mts to support the configuration and testing of the GCP KMS integration.
  • Dependency Updates: Updated server/package.json to include @google-cloud/kms and @valora/viem-account-hsm-gcp, providing the necessary libraries for interacting with GCP KMS and integrating HSM-backed accounts with Viem.
Changelog
  • .changeset/lucky-jokes-change.md
    • Added a new changeset file to document the integration of GCP KMS for the allower functionality.
  • .changeset/silly-yaks-divide.md
    • Added a new changeset file to document the feature of poking accounts after KYC completion.
  • .do/app.yaml
    • Added new environment variables for GCP KMS configuration, including key ring, key version, project ID, and base64 encoded JSON credentials.
  • cspell.json
    • Added 'valora' to the custom spelling dictionary.
  • server/hooks/activity.ts
    • Removed unused ABI imports and the withRetry utility from viem.
    • Updated the import path for keeper to the new ../utils/accounts module.
    • Renamed accounts variable to accountLookup for clarity in database queries.
    • Modified the logic for processing transfers, replacing pokes map with a Set of accounts.
    • Refactored the account creation and poking logic to use the new keeper.poke function, simplifying asset handling and error management.
  • server/hooks/block.ts
    • Updated the import path for keeper to the new ../utils/accounts module.
  • server/hooks/panda.ts
    • Updated the import path for keeper to the new ../utils/accounts module.
  • server/hooks/persona.ts
    • Imported firewallAddress from @exactly/common/generated/chain.
    • Imported allower and keeper from the new ../utils/accounts module.
    • Added a getAllower function to lazily initialize the allower client.
    • Integrated allower().then((client) => client.allow(account)) call after risk assessment to allow the account through the firewall.
    • Added a keeper.poke call after KYC to update account assets with a notification.
    • Refactored the addCapita call to directly use the parsed account address, removing the safeParse check.
  • server/package.json
    • Added @google-cloud/kms as a dependency.
    • Added @valora/viem-account-hsm-gcp as a dependency.
  • server/script/openapi.ts
    • Added stub environment variables for GCP KMS configuration.
  • server/test/api/card.test.ts
    • Updated the mock import from ../mocks/keeper to ../mocks/accounts.
  • server/test/e2e.ts
    • Updated the mock import from ../mocks/keeper to ../mocks/accounts.
  • server/test/hooks/activity.test.ts
    • Updated the mock import from ../mocks/keeper to ../mocks/accounts.
    • Removed the test case for capturing 'no balance' errors after retries.
    • Adjusted captureException expectation to be more general.
    • Added anvilClient.setBalance calls to various tests to ensure sufficient balance for transactions.
    • Replaced exaSend spy with pokeSpy for keeper interactions.
    • Added a new test case to verify poke is called with the correct ignore option.
  • server/test/hooks/block.test.ts
    • Updated the mock import from ../mocks/keeper to ../mocks/accounts.
  • server/test/hooks/panda.test.ts
    • Updated the mock import from ../mocks/keeper to ../mocks/accounts.
  • server/test/hooks/persona.test.ts
    • Added mocks for accounts and firewallAddress.
    • Updated afterEach hook to restore all mocks.
    • Updated persona signature header in test payloads.
    • Added tests for poking assets when balances are positive, poking only ETH, skipping WETH when ETH balance is positive, and not poking when balances are zero.
    • Added a test case to verify error handling when the firewall call fails.
    • Added a mock for panda.createUser in the manteca template test.
  • server/test/mocks/accounts.ts
    • Renamed server/test/mocks/keeper.ts to server/test/mocks/accounts.ts.
    • Updated the mock to include allower and keeper from the new accounts module.
    • Configured allower mock to resolve with an allow function.
  • server/test/utils/accounts.test.ts
    • Renamed server/test/utils/keeper.test.ts to server/test/utils/accounts.test.ts.
    • Updated imports to reference the new accounts mock and module.
    • Updated type imports to use @exactly/common/validation for Hash.
  • server/test/utils/gcp.test.ts
    • Added a new test file to verify GCP credentials security, including file creation with secure permissions and early return for existing credentials.
  • server/utils/accounts.ts
    • Added a new file to define keeper and allower wallet clients.
    • Implemented extender function to add poke functionality to the wallet client, handling ETH and ERC20 asset poking based on balances.
    • Implemented withExaSend function for robust transaction sending with Sentry tracing, error handling, and nonce management.
    • Integrated GCP KMS for the allower client, including credential initialization and retry logic for KMS errors.
    • Defined getAccount to retrieve a local account from GCP HSM.
  • server/utils/gcp.ts
    • Added a new file to manage GCP credentials, including base64 decoding and secure file writing.
    • Implemented initializeGcpCredentials to ensure GCP service account credentials are set up securely.
    • Added hasCredentials to check for the existence of the credentials file.
    • Provided isRetryableKmsError to identify transient KMS errors for retry mechanisms.
  • server/utils/keeper.ts
    • Removed the file, as its functionality has been refactored into server/utils/accounts.ts.
  • server/vitest.config.mts
    • Added GCP KMS related environment variables for Vitest testing.
Activity
  • The pull request introduces new functionality and refactors existing code, indicating active development.
  • New changeset files were added, suggesting that these changes are intended for release notes.
  • Extensive test updates and additions were made, reflecting thorough testing of the new allower and refactored accounts modules.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

gemini-code-assist[bot]

This comment was marked as resolved.

coderabbitai[bot]

This comment was marked as resolved.

@sentry
Copy link

sentry bot commented Feb 25, 2026

Codecov Report

❌ Patch coverage is 77.51938% with 58 lines in your changes missing coverage. Please review.
✅ Project coverage is 71.04%. Comparing base (05716e4) to head (a4d043e).
⚠️ Report is 2 commits behind head on main.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
server/utils/accounts.ts 68.53% 30 Missing and 15 partials ⚠️
server/hooks/activity.ts 92.18% 3 Missing and 2 partials ⚠️
server/utils/gcp.ts 85.29% 3 Missing and 2 partials ⚠️
server/hooks/persona.ts 82.35% 1 Missing and 2 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #839      +/-   ##
==========================================
- Coverage   71.13%   71.04%   -0.09%     
==========================================
  Files         211      212       +1     
  Lines        8355     8502     +147     
  Branches     2724     2766      +42     
==========================================
+ Hits         5943     6040      +97     
- Misses       2133     2164      +31     
- Partials      279      298      +19     
Flag Coverage Δ
e2e 66.29% <57.36%> (-0.80%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

coderabbitai[bot]

This comment was marked as resolved.

coderabbitai[bot]

This comment was marked as resolved.

coderabbitai[bot]

This comment was marked as resolved.

coderabbitai[bot]

This comment was marked as resolved.

@aguxez aguxez marked this pull request as ready for review February 25, 2026 23:38
greptile-apps[bot]

This comment was marked as resolved.

chatgpt-codex-connector[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

@aguxez aguxez force-pushed the gcp-allower branch 2 times, most recently from c642d46 to 88aa6aa Compare March 2, 2026 12:51
sentry[bot]

This comment was marked as resolved.

chatgpt-codex-connector[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

greptile-apps[bot]

This comment was marked as resolved.

sentry[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

greptile-apps[bot]

This comment was marked as resolved.

chatgpt-codex-connector[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

greptile-apps[bot]

This comment was marked as resolved.

@aguxez aguxez force-pushed the gcp-allower branch 2 times, most recently from d4deb72 to e474bf6 Compare March 3, 2026 16:02
sentry[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

chatgpt-codex-connector[bot]

This comment was marked as resolved.

greptile-apps[bot]

This comment was marked as resolved.

chatgpt-codex-connector[bot]

This comment was marked as resolved.

greptile-apps[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

@greptile-apps
Copy link

greptile-apps bot commented Mar 3, 2026

Additional Comments (1)

server/utils/accounts.ts, line 155
market may be null at the type level in args

assetsToPoke is typed as { asset: Address; market: Address | null }[]. When destructured at line 136, TypeScript sees market as Address | null in both branches of the ternary (lines 145–156). The else branch passes args: [market] to exaSend, where null is not a valid Address.

At runtime this is safe — only ETH entries have market: null and they take the pokeETH branch (line 149) — but TypeScript does not narrow market based on the asset !== ETH check. A future refactor that changes how assetsToPoke is populated could silently introduce a null address being sent on-chain, and the failure would be swallowed inside the outer Promise.allSettled (line 135).

Consider asserting non-null at the call site to make the invariant explicit:

                  : {
                      address: accountAddress,
                      abi: combinedAccountAbi,
                      functionName: "poke",
                      // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
                      args: [market!],
                    },

Or narrow the type at build time by storing ETH entries and market entries in separate arrays.

Copy link

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

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

Devin Review found 1 new potential issue.

View 14 additional findings in Devin Review.

Open in Devin Review

Comment on lines +164 to +166
await keeper
.poke(account, { ignore: [`NotAllowed(${account})`] })
.catch((error: unknown) => captureException(error, { level: "error" }));

Choose a reason for hiding this comment

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

🚩 NoBalance() special handling removed without replacement

The old activity.ts had a specific retry-and-capture flow for NoBalance() errors: it used { ignore: ["NoBalance()"] } in exaSend, then re-threw inside .then() to trigger withRetry, and finally captured the error as a warning (not error) if all retries were exhausted.

The new code passes { ignore: [\NotAllowed(${account})`] }instead, meaningNoBalance()is no longer ignored byexaSend. If a poke reverts with NoBalance(), the withRetryinsidepoke (server/utils/accounts.ts:137-163) will retry it (default retry behavior), but each retry's failure is captured at errorlevel viaexaSend's catch block — previously it was only warning` after all retries.

The corresponding test (captures no balance once after retries) was also removed. This seems intentional per the PR's simplification goals, but changes error noise characteristics in Sentry.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

@aguxez aguxez force-pushed the gcp-allower branch 2 times, most recently from 17aa7dc to 115a36a Compare March 3, 2026 19:12
@greptile-apps
Copy link

greptile-apps bot commented Mar 3, 2026

Additional Comments (2)

server/utils/gcp.ts, line 37
Stale initializationPromise after credentials file deletion

initializationPromise is cached after the first successful initialization. If the credentials file is later removed at runtime (e.g., OS /tmp cleanup, systemd-tmpfiles, long-running container with volatile /tmp), every subsequent call to initializeGcpCredentials() returns the cached resolved promise without attempting to recreate the file.

Downstream in getAccount():

await initializeGcpCredentials();       // returns cached no-op promise
if (!(await hasCredentials())) throw   // file is gone → always throws

The server cannot recover without a full restart. The resetGcpInitialization() export exists but is never called automatically, leaving allower() unable to recover from runtime credential file loss.

Suggested fix: Call resetGcpInitialization() inside getAccount() if hasCredentials() returns false after initialization resolves, allowing the next attempt to re-decode and re-write the credentials file:

export async function getAccount(): Promise<LocalAccount> {
  await initializeGcpCredentials();
  if (!(await hasCredentials())) {
    resetGcpInitialization(); // allow next caller to retry
    throw new Error();
  }
  
}

server/utils/accounts.ts, line 63
Module-level GCP validation breaks keeper when GCP is unavailable

These GCP env-var checks run at module import time, before any GCP-dependent code is invoked. keeper (defined at line 65) is a plain private-key wallet client with no GCP dependency. Any hook that imports keeper (activity.ts, block.ts, panda.ts, etc.) will now fail to start if GCP_PROJECT_ID, GCP_KMS_KEY_RING, or GCP_KMS_KEY_VERSION are absent — even in environments or unit tests where the allower is not used.

This breaks the deployment of unrelated features if GCP credentials are not configured, and makes testing hooks that use keeper harder in isolated environments.

Suggested fix: Move the GCP env-var validation inside getAccount(), which is the only function that actually requires them:

export async function getAccount(): Promise<LocalAccount> {
  if (!process.env.GCP_PROJECT_ID) throw new Error("GCP_PROJECT_ID is required");
  const projectId = process.env.GCP_PROJECT_ID;
  if (!/^[a-z][a-z0-9-]{4,28}[a-z0-9]$/.test(projectId)) {
    throw new Error("GCP_PROJECT_ID must be a valid GCP project ID format");
  }
  // … validate GCP_KMS_KEY_RING and GCP_KMS_KEY_VERSION here too
  await initializeGcpCredentials();
  
}

This keeps keeper importable regardless of GCP configuration and surfaces the error at the point of use.

chatgpt-codex-connector[bot]

This comment was marked as resolved.

@greptile-apps
Copy link

greptile-apps bot commented Mar 3, 2026

Additional Comments (3)

server/utils/accounts.ts, line 161
Bitwise left-shift overflow in retry delay

1 << count uses a 32-bit signed integer operation. For count >= 31, the result wraps to a negative number (1 << 31 = -2147483648), which would produce a large negative delay and cause immediate (potentially infinite) retries without any back-off. With retryCount: 10 the current cap of count = 9 avoids the overflow, but this is a silent footgun if retryCount is ever raised.

Prefer the explicit exponentiation operator instead:

            delay: ({ count }) => Math.trunc(2 ** count) * 60,

server/utils/gcp.ts, line 4
DECODING_ITERATIONS magic number is undocumented

The triple base64 decode (DECODING_ITERATIONS = 3) is an unusual encoding strategy that will silently produce a corrupted credentials file if GCP_BASE64_JSON is encoded with a different iteration count (e.g., a single base64 encode as would be typical). There is no comment explaining why three iterations are required, and the only clue is hidden in the config-level test value.

Consider adding a comment to the constant (and possibly the app.yaml instructions) documenting the expected encoding:

// Credentials are stored triple-base64-encoded (base64(base64(base64(json)))) to survive
// shell/environment variable escaping layers. Ensure GCP_BASE64_JSON is encoded accordingly.
const DECODING_ITERATIONS = 3;

server/utils/accounts.ts, line 72
keeper onFetchRequest handler lacks error handling unlike allower

The allower client's onFetchRequest wraps captureRequests in a try/catch and logs failures with captureMessage (lines 385-394). The keeper client's handler does not — if clone.json() or parse(Requests, …) throws (e.g., malformed RPC batch payload), the error propagates out of the transport hook and can unexpectedly fail the entire RPC call.

      async onFetchRequest(request) {
        try {
          const clone = request.clone();
          captureRequests(parse(Requests, await clone.json()));
        } catch (error: unknown) {
          captureMessage("failed to parse or capture rpc requests", {
            level: "error",
            extra: { error },
          });
        }
      },

devin-ai-integration[bot]

This comment was marked as resolved.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

server: poke account after allowing it in the firewall

2 participants