Skip to content

feat(config): API prefix, JWT secret hardening, RPC/network check, secret providers - #354

Merged
EmmanuelOchaje merged 2 commits into
StellarTickets:mainfrom
UFObject247:feat/250-253-config-hardening
Oct 4, 2026
Merged

EmmanuelOchaje merged 2 commits into
StellarTickets:mainfrom
UFObject247:feat/250-253-config-hardening

Conversation

@UFObject247

@UFObject247 UFObject247 commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

Closes #250
Closes #251
Closes #252
Closes #253

Summary

#250: configurable global API prefix

  • New optional API_PREFIX setting, e.g. api/v1. main.ts applies it with setGlobalPrefix, so POST /auth/login becomes POST /api/v1/auth/login.
  • GET /health is excluded and stays at the root.
  • Leading and trailing slashes are ignored, and an empty value means no prefix. Env validation rejects values that aren't URL path segments, including . and .. segments.
  • Test: src/config/api-prefix.spec.ts starts a Nest app with supertest. It checks that routes are served under the prefix, that /health stays at the root, and that nothing changes when the variable is unset.

#251: fail startup when JWT_SECRET is weak

  • Every environment keeps the 32-character minimum.
  • With NODE_ENV=production, startup also fails when the secret contains a known placeholder (changeme, yoursecret, jwtsecret, example, password, …) or has fewer than 10 distinct characters.
  • The same rules run when the secret is loaded through a secret provider (see Add support for a secrets manager for the JWT secret #253), not only when it comes from the environment.
  • The spec covers accepted and rejected values in production, and confirms that development is unchanged.

#252: SOROBAN_RPC_URL / network pairing

  • The app derives the signing passphrase from STELLAR_NETWORK. At boot, env validation now infers which network the RPC URL serves from its host or path (testnet, futurenet, mainnet, pubnet). If that network differs from STELLAR_NETWORK, startup fails.
  • URLs that name no network (self-hosted or local nodes), or more than one, are allowed. The RPC URL must be a valid http or https URL.
  • The network-to-passphrase table moved from StellarService to src/config/stellar-networks.ts, so signing and validation use the same table.
  • Specs: stellar-networks.spec.ts, plus mismatch and valid cases in env.validation.spec.ts.

#253: pluggable secret provider for the JWT secret

  • New SecretProvider interface and a global SecretsModule. The provider is chosen with SECRETS_PROVIDER:
    • env (default): reads the JWT_SECRET environment variable.
    • file: reads the file at JWT_SECRET_FILE. This covers Docker/Kubernetes secrets and sidecars such as Vault Agent or the Secrets Store CSI drivers.
    • custom: uses a class passed to SecretsModule.forRoot({ provider }).
  • JwtSecretModule resolves the secret once at boot. JwtModule (signing) and JwtStrategy (verification) now both get it from there instead of each reading JWT_SECRET from ConfigService.
  • Env validation requires JWT_SECRET only for env, and JWT_SECRET_FILE only for file.
  • Documented in docs/CONFIGURATION.md ("Secret providers", with an example custom provider), .env.example and docs/DEPLOYMENT.md.
  • Specs: secrets.module.spec.ts covers choosing each provider, reading and trimming files, and the error when custom has no provider. jwt-secret.module.spec.ts resolves the secret through a custom provider and checks that AuthModule signs and verifies tokens with it.

Testing

Rebased on the latest main (after #349, #351 and #352).

  • npx jest: 51 suites, 310 of 311 tests pass. main alone has 47 suites and 244 tests. This PR adds the new specs, and all of them pass.
  • Two suites also fail on main, and this PR doesn't touch them:
    • bigint-serializer.interceptor.spec.ts: 1 test fails.
    • pending-tx-cleanup.service.spec.ts: can't run because @nestjs/schedule is not installed.
  • npx eslint on all changed files: clean.
  • npx tsc --noEmit: the same 15 errors as main, none new and none in files this PR touches.

Upgrade notes

  • Existing .env files keep working. SECRETS_PROVIDER defaults to env and API_PREFIX defaults to no prefix.
  • A production deployment whose JWT_SECRET is a placeholder, or whose RPC URL names a different network than STELLAR_NETWORK, will now refuse to start. This is intended.

@netlify

netlify Bot commented Sep 24, 2026 •

Copy link
Copy Markdown

✅ Deploy Preview for stellarticketsbackend ready!

Name Link
🔨 Latest commit 2aae798
🔍 Latest deploy log https://app.netlify.com/projects/stellarticketsbackend/deploys/6abf8320812e0b00087e8f0a
😎 Deploy Preview https://deploy-preview-354--stellarticketsbackend.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

…cret providers

- API_PREFIX mounts every route under a path prefix via setGlobalPrefix,
  with GET /health excluded so probes keep working (StellarTickets#250).
- JWT_SECRET must be 32+ characters everywhere; in production it must also
  not be a known placeholder and must have 10+ distinct characters (StellarTickets#251).
- Boot fails when SOROBAN_RPC_URL names a different network than
  STELLAR_NETWORK, since the passphrase is derived from STELLAR_NETWORK;
  the passphrase table now lives in src/config/stellar-networks.ts (StellarTickets#252).
- JWT_SECRET is resolved at boot through a pluggable SecretProvider:
  env (default), file (<KEY>_FILE), or a custom provider passed to
  SecretsModule.forRoot. Signing and verification share the resolved
  secret, and the strength rules apply to it whatever the source (StellarTickets#253).

Closes StellarTickets#250
Closes StellarTickets#251
Closes StellarTickets#252
Closes StellarTickets#253
@UFObject247
UFObject247 force-pushed the feat/250-253-config-hardening branch from 8411b8c to 5c9a3e4 Compare September 24, 2026 18:21
@drips-wave

drips-wave Bot commented Sep 24, 2026

Copy link
Copy Markdown

@UFObject247 Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@EmmanuelOchaje
EmmanuelOchaje merged commit 059b731 into StellarTickets:main Oct 4, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants