Skip to content

🧱 server: business subdomain on testnets - #1296

Open
nfmelendez wants to merge 6 commits into
panda-b2b-approvalsfrom
subdomain
Open

nfmelendez wants to merge 6 commits into
panda-b2b-approvalsfrom
subdomain

Conversation

@nfmelendez

@nfmelendez nfmelendez commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

summary

add the business subdomain alias on testnet environments.

stacked on #1209.

changes

  • add `business.${APP_DOMAIN}` as an alias domain in `.do/app.yaml`, enabled only on `base-sepolia` and `sandbox`

test plan

  • deploy to base-sepolia and verify `business.` subdomain resolves to the app

🤖 Generated with Claude Code

https://claude.ai/code/session_01WRNy8HTnB5tWJWsg3DBBb1

Summary by CodeRabbit

  • New Features
    • Added environment-specific support for the business subdomain in Base Sepolia and Sandbox deployments.

@changeset-bot

changeset-bot Bot commented Sep 9, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 4500b2f

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

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

@coderabbitai

coderabbitai Bot commented Sep 9, 2026

Copy link
Copy Markdown

Review Change StackReview Change Stack

Important

Review skipped

We couldn't safely recover the incremental review. No full review was started, and the last reviewed checkpoint was preserved. Retry later, or explicitly request a full review by commenting @coderabbitai full review.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Walkthrough

The DigitalOcean App Platform configuration now adds a business.${{ env.APP_DOMAIN }} alias for the base-sepolia and sandbox environments.

Changes

Domain alias configuration

Layer / File(s) Summary
Conditional business domain alias
.do/app.yaml
Adds a guarded business.${{ env.APP_DOMAIN }} alias domain for base-sepolia and sandbox environments.

Priority: ⬇️ Low

Estimated code review effort: 1 (Trivial) | ~2 minutes

Suggested reviewers: aguxez

Merge Risk: 🟡 Moderate · up to 4500b

This change intends to expose the business subdomain on testnets, but deployments will continue using the existing app configuration, leaving the alias unavailable. Update the deployment path to apply the rendered app specification before merging.

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the addition of the business subdomain for testnet environments. It is concise and directly related to the main change.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch subdomain
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch subdomain

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Advanced

Run ID: 15be5985-211d-4bea-b102-5b1d8abb8498

📥 Commits

Reviewing files that changed from the base of the PR and between 703bb80 and 4500b2f.

📒 Files selected for processing (1)
  • .do/app.yaml

Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.

Comment thread .do/app.yaml
Comment on lines +27 to +28
# @if ${{ env.ENVIRONMENT == "base-sepolia" || env.ENVIRONMENT == "sandbox" }}
- domain: business.${{ env.APP_DOMAIN }}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🔎 Supported by static analysis

🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

rg -n -C 5 \
  'digitalocean/app_action/deploy@v2|app_spec_location|`@if`|`@endif`|ENVIRONMENT|APP_DOMAIN' \
  .github .do

ref="$(git ls-remote https://github.com/digitalocean/app_action.git refs/heads/v2 refs/tags/v2 | awk 'NR == 1 { print $1 }')"
test -n "$ref"

curl --fail --silent --show-error \
  "https://raw.githubusercontent.com/digitalocean/app_action/${ref}/deploy/main.go" |
  rg -n 'ExpandEnv|yaml.Unmarshal|`@if`|`@endif`'

Repository: exactly/exa

Length of output: 9622


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

printf '%s\n' '--- .do/render.js ---'
cat -n .do/render.js

printf '%s\n' '--- deployment workflow ---'
sed -n '44,75p' .github/workflows/server-deploy.yaml

printf '%s\n' '--- renderer references ---'
rg -n -C 3 'render\.js|app_spec_location|\.do/app\.yaml|ENVIRONMENT' .github .do package.json Makefile 2>/dev/null || true

printf '%s\n' '--- action refs ---'
git ls-remote --heads --tags https://github.com/digitalocean/app_action.git 'v2*' | head -20

Repository: exactly/exa

Length of output: 9244


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

python3 - <<'PY'
from pathlib import Path
p = Path(".do/render.js")
print(p.read_text())
PY

Repository: exactly/exa

Length of output: 3520


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

commit='cc55bc9b848d25f9c1c9f831cf843fbab3fbfb15'
base="https://raw.githubusercontent.com/digitalocean/app_action/${commit}"

printf '%s\n' '--- pinned tag target ---'
curl --fail --silent --show-error \
  "https://api.github.com/repos/digitalocean/app_action/commits/${commit}" |
  python3 -c 'import json,sys; print(json.load(sys.stdin)["sha"])'

printf '%s\n' '--- action metadata ---'
curl --fail --silent --show-error "$base/action.yml"

printf '%s\n' '--- action implementation references ---'
for file in deploy/main.go deploy/action.yml main.go; do
  if curl --fail --silent --show-error "$base/$file" > /tmp/action-file; then
    printf '%s\n' "--- $file ---"
    rg -n -C 3 'app_spec|yaml|Expand|env|spec' /tmp/action-file || true
  fi
done

Repository: exactly/exa

Length of output: 291


🤖 get_repo_knowledge executed:

get_repo_knowledge exactly/exa /tmp/coderabbit-repo-knowledge/exactly-exa-f26ca19b/learnings /tmp/coderabbit-repo-knowledge/exactly-exa-f26ca19b/conventions

Length of output: 28018


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

commit='cc55bc9b848d25f9c1c9f831cf843fbab3fbfb15'
curl --fail --silent --show-error \
  "https://api.github.com/repos/digitalocean/app_action/git/trees/${commit}?recursive=1" |
  python3 -c '
import json, sys
tree = json.load(sys.stdin)["tree"]
for item in tree:
    path = item["path"]
    if path.endswith((".yml", ".yaml", ".go")):
        print(path)
'

Repository: exactly/exa

Length of output: 593


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

commit='cc55bc9b848d25f9c1c9f831cf843fbab3fbfb15'
base="https://raw.githubusercontent.com/digitalocean/app_action/${commit}"

printf '%s\n' '--- deploy/action.yml ---'
curl --fail --silent --show-error "$base/deploy/action.yml"

printf '%s\n' '--- deploy/main.go ---'
curl --fail --silent --show-error "$base/deploy/main.go" |
  rg -n -C 5 'appSpec|app_spec|spec|yaml|Expand|env|Parse|Unmarshal|ReadFile|inputs'

Repository: exactly/exa

Length of output: 7604


Deploy the rendered app spec.

The workflow passes app_name to digitalocean/app_action/deploy@v2. In this mode, the action fetches the existing app and uses its remote app.Spec; it ignores .do/app.yaml. Therefore, this alias change is not deployed. Check out the repository, render .do/app.yaml, pass the result through app_spec_location, and remove app_name.

Source: MCP tools

@nfmelendez
nfmelendez added this pull request to stack #1265 September 9, 2026 12:26
@codecov

codecov Bot commented Sep 9, 2026

Copy link
Copy Markdown

⚠️ JUnit XML file not found

The CLI was unable to find any JUnit XML files to upload.
For more help, visit our troubleshooting guide.

@nfmelendez nfmelendez mentioned this pull request Sep 9, 2026
2 tasks

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7834d43ab7

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread .do/app.yaml
domains:
- domain: ${{ env.APP_DOMAIN }}
type: PRIMARY
# @if ${{ env.ENVIRONMENT == "base-sepolia" || env.ENVIRONMENT == "sandbox" }}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Use a conditional syntax supported by the renderer

Rendering this app spec now fails for every environment: .do/render.js only accepts bare environment variables or a single ==/!= comparison, so this || expression throws unsupported @if expression even with ENVIRONMENT=base-sepolia. This blocks the DigitalOcean spec from being generated; express the alternatives with supported directives or extend the renderer to handle logical operators.

Useful? React with 👍 / 👎.

Comment thread .do/app.yaml
- domain: ${{ env.APP_DOMAIN }}
type: PRIMARY
# @if ${{ env.ENVIRONMENT == "base-sepolia" || env.ENVIRONMENT == "sandbox" }}
- domain: business.${{ env.APP_DOMAIN }}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Allow the alias origin through the application stack

After this alias becomes deployable, opening business.sandbox.exactly.app or business.base-sepolia.exactly.app serves the web bundle, but that bundle is built with the primary APP_DOMAIN and sends API requests to https://${domain} (src/utils/server.ts:74). Those requests therefore originate from the new business host while CORS and CSRF accept only the primary appOrigin (server/api/index.ts:71-75), and WebAuthn verification likewise accepts only that origin, so browser API and authentication flows on the new alias are rejected. Add the business origin throughout the origin validation path or make the web client use a supported same-origin endpoint.

Useful? React with 👍 / 👎.

Comment thread .do/app.yaml
- domain: ${{ env.APP_DOMAIN }}
type: PRIMARY
# @if ${{ env.ENVIRONMENT == "base-sepolia" || env.ENVIRONMENT == "sandbox" }}
- domain: business.${{ env.APP_DOMAIN }}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Select the business account flow on the alias

Even after the alias origin is accepted, loading this hostname does not select the business flow: the target tree contains no client-side hostname check or account-type header, and the registration calls in src/utils/server.ts:243-263 send only the session header. The server relies on account-type: business to choose the nonzero business salt (server/api/auth/registration.ts:411-415) and to enter business KYC (server/api/kyc.ts:579-583), so users entering through the new business URL are instead created and onboarded as individual accounts. Derive the account type from the alias and propagate it on the relevant requests.

Useful? React with 👍 / 👎.

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.

2 participants