hubODSEA
SecurityMay 30, 2026•14 min read

Secrets Management for Startups That Can't Afford a Security Breach

The .env file committed to GitHub has cost startups millions. Infisical fixes the problem permanently — and it takes less than 20 minutes to integrate with Vercel. Here's exactly how we did it.

O

ODSEA Team

Secrets Management for Startups That Can't Afford a Security Breach

Every startup makes this mistake at some point. It is so common that GitHub has a built-in secret scanning service specifically to catch it after the fact. The mistake is committing a .env file to a version-controlled repository.

The .env file contains API keys. Database connection strings with credentials embedded. Stripe secret keys that can process charges without limits. AWS access keys that can spin up infrastructure or read S3 buckets full of user data. JWT signing secrets that allow forging authenticated sessions. In some cases, it contains the keys to services that bill by usage — and whose bills arrive before anyone notices the breach.

The damage from an exposed secret does not wait for you to notice. Automated bots scan GitHub — including private repositories when access tokens are leaked — within minutes of a commit. Truffles can find credentials in commit history that predate the current developer by years. The OWASP API Security Project consistently ranks "Broken Object Level Authorization" and "Broken Authentication" in the top positions on its threat list, and compromised credentials are the most common entry point for both.

This post documents the specific approach ODSEA uses in production — including the exact Infisical integration with Vercel and the infisical run command pattern used in our database migration workflow. Everything here is implementable in under an hour for a project that doesn't yet have a secrets management system.

Part 1: How Secrets Get Exposed — The Real Scenarios

Before the solution, the failure modes. Understanding how secrets get exposed is the prerequisite for preventing it.

Scenario 2: The Freelancer Handoff

A startup hires a contractor to build a feature. The contractor, working from a local clone, creates a .env file to make the project run. They complete the work, submit a pull request, and offboard. Six months later, the startup undergoes a security audit. The auditor discovers that the contractor's .env file was committed in a merge commit three months ago — after the contractor's access was revoked — and has been sitting in the repository's history ever since. The database password exposed was never rotated because nobody noticed the exposure.

Scenario 3: The CI/CD Environment Variable Dump

A developer debugging a failing CI/CD pipeline adds a step that dumps environment variables to the build log for inspection. The pipeline uses GitHub Actions with public logging. The log is visible to anyone with read access to the repository. The environment variables include the production Supabase service role key, which bypasses row-level security.

In 2026, several high-profile breaches at startups traced their initial entry vector to a CI/CD log that had been mistakenly set to public. Logs feel ephemeral; they are not.

Scenario 4: The Leaked Access Token

A developer is authenticated on their machine with a GitHub personal access token scoped to read private repositories. That token is stored in a config file. A malicious package installed via npm install reads the config file and exfiltrates the token. The attacker now has read access to every private repository the developer has access to — including the one with the .env file in the commit history that was never fully purged.

The pattern across all four scenarios: secrets stored in version control or in locations that are accessible to more systems than necessary are a liability that compounds over time.


Part 2: What Infisical Is and Why It Solves the Problem Structurally

Infisical is an open-source secrets management platform. Its job is to be the single, secure location where secrets live, with fine-grained access control that determines which system — a developer's laptop, a Vercel deployment, a GitHub Actions workflow — can read which secrets at which stage.

The key architectural shift that Infisical enables is moving from a model where secrets travel with code to a model where secrets are injected into processes at runtime, never stored in files that can be committed.

At ODSEA, every environment in the system — development, staging, and production — has its own isolated secret store in Infisical. A developer working locally runs commands prefixed with infisical run --env=dev --. That prefix intercepts the command, fetches the appropriate secrets from Infisical's secure API, injects them as environment variables for the duration of the command, and then discards them. The secrets never touch the filesystem.

The practical consequence is that a developer can clone the ODSEA repository and have zero access to the production database until they are granted explicit permission in Infisical. Even if they have full repository access. Even if they can read every file in the codebase. The production secrets are simply not in the repository — they never have been.


Here is the implementation as it actually runs in the ODSEA stack.

Authentication Setup

Infisical uses machine identity tokens for CI/CD and service-to-service authentication, and user identity tokens for developer workstation access. For local development:

# Authenticate the Infisical CLI on a developer's machine
infisical login

# Link the project (run once in the repository root)
infisical init

The infisical init command creates a .infisical.json file in the project root that contains the project ID — not the secrets themselves. This file is safe to commit. It tells the CLI which Infisical project to pull secrets from when a command is run.

The Command Prefix Pattern

Every command that requires environment variables is prefixed with infisical run --env=[environment] --. Some examples from the ODSEA codebase:

# Local development server
# Type checking with secrets available
infisical run --env=dev -- bun run typecheck

# Production builds (run in CI, not locally)
infisical run --env=prod -- bun run build

The drizzle.config.ts example is instructive. Drizzle's configuration file needs a database connection string to run migrations. In a .env-based system, that connection string lives in a file on the developer's machine. In the Infisical model, the connection string is fetched from Infisical's API at the moment the migration runs and injected into the Drizzle process. The drizzle.config.ts file reads process.env.DATABASE_URL — which is populated by Infisical — not from a file that could be accidentally committed.

// drizzle.config.ts — reads from process.env populated by infisical run
import type { Config } from 'drizzle-kit';

export default {
  schema: './src/db/schema.ts',
  out: './src/migrations',
  driver: 'pg',
  dbCredentials: {
    connectionString: process.env.DATABASE_URL!,
  },
} satisfies Config;

When this runs via infisical run --env=dev -- bun run drizzle-kit migrate, the DATABASE_URL is populated with the development database connection string from Infisical — not from any file on disk.

Environment Separation

Infisical organizes secrets into environments. ODSEA uses three: dev, staging, and prod. Each environment has its own set of secrets, and access to each environment is controlled independently.

Development (dev): Accessible to all team members. Contains credentials for a development Supabase instance, test API keys (Stripe test mode, SendGrid sandbox), and development service URLs.

Staging (staging): Accessible to senior developers and CI/CD pipelines. Contains credentials for the staging Supabase instance and sandbox/test credentials for external services.

Production (prod): Accessible only to CI/CD pipelines and senior team members by explicit grant. Contains live production credentials. No developer access is granted by default — it must be requested, approved, and time-limited.

This access model means that a junior developer whose machine is compromised cannot access production credentials even if the attacker has full control of the machine and its Infisical authentication token. The token only grants access to dev environment secrets.

Part 4: Vercel Integration — The 20-Minute Setup

The Vercel + Infisical integration removes the need to manually manage environment variables in the Vercel dashboard. Instead, Infisical syncs secrets directly to Vercel on a per-environment basis.

Install the Infisical CLI (Windows)

winget install Infisical.infisical

Or via Homebrew (macOS/Linux)

brew install infisical/get-cli/infisical


### Vercel Integration Setup

In the Infisical dashboard, navigate to **Project → Integrations → Vercel**. You will need:

1. Your Vercel API token (from Vercel dashboard → Account Settings → Tokens)
2. Your Vercel team ID and project ID

Once connected, configure the sync:

- Infisical `dev` → Vercel `Preview` environments
- Infisical `staging` → Vercel `Preview` environments (for staging branches)
- Infisical `prod` → Vercel `Production` environment

After configuration, any secret update in Infisical triggers an automatic sync to the appropriate Vercel environment. You never touch the Vercel dashboard for secret management again. The Vercel dashboard becomes a read-only view — the source of truth is Infisical.

The sync also runs in reverse during initial setup: you can import existing Vercel environment variables into Infisical as a migration step, so you do not need to re-enter every secret manually.

### GitHub Actions Integration

For CI/CD workflows that run outside Vercel, Infisical integrates with GitHub Actions via a machine identity token:

    client-id: ${{ secrets.INFISICAL_CLIENT_ID }}
    client-secret: ${{ secrets.INFISICAL_CLIENT_SECRET }}
    env-slug: prod

Note: `INFISICAL_CLIENT_ID` and `INFISICAL_CLIENT_SECRET` are the only secrets that need to be stored in GitHub's own secret store — everything else flows through Infisical. This is the minimum necessary footprint: you cannot entirely eliminate the bootstrap secret, but you can reduce what is stored outside Infisical to a single authentication credential.


## Part 5: Secret Rotation Without Downtime

1. **Add the new secret** to Infisical under a versioned key (e.g., `DATABASE_URL_v2`) while keeping the current key active.
2. **Update the application** to read both the old and new key, accepting authentication from either.
3. **Deploy** the application update.
4. **Rotate** the actual credential at the service provider (Supabase, Redis, etc.) to the new value.
5. **Remove the old key** from Infisical and update the application to read only the new key.
6. **Deploy** the cleanup.

This protocol requires two deployments per rotation but eliminates any gap where the application has neither the old nor the new valid credential. For less critical secrets — third-party API keys, webhook signing secrets — a simpler single-swap rotation is sufficient, accepting a brief period of potential failures that will self-resolve on the next request.

Infisical supports secret versioning natively, so the old version of a secret is never fully lost — it is archived and auditable. If a rotation creates an unexpected failure, rollback requires only pointing the application back to the previous version, without any credential re-provisioning.

### Rotation Triggers

The ODSEA security policy triggers a secret rotation in the following circumstances:

- Any team member offboards (revoke their personal access tokens, rotate any shared credentials they had access to)
- Any CI/CD machine identity token is suspected of exposure
- Any security scanner flags a potential exposure in repository history
- Annually as a baseline hygiene measure, even without a specific trigger

The rotation cost with Infisical is primarily the time to update the actual credential at the service provider. The propagation to all environments, all CI/CD workflows, and all Vercel deployments happens automatically through the sync integration.

---

## Part 6: What Infisical Cannot Do — The Remaining Risks

Infisical solves the secrets-in-version-control problem comprehensively. It does not solve every secrets management problem.

**It does not prevent credential sharing over insecure channels.** Developers who share credentials via Slack messages, emails, or screenshots bypass the entire system. The human vector remains. Solving it requires organizational policy, not tooling.

**It does not prevent over-privileged access tokens.** If a machine identity token is granted access to the production environment when it only needs staging access, compromising that token exposes production. The principle of least privilege must be applied manually — Infisical provides the mechanisms but does not enforce scope automatically.

**It does not protect secrets in application memory.** A secret injected into a process can be extracted from that process's memory by code running in the same environment. Secrets should be treated as resident in memory only for as long as they are needed, and never logged, serialized, or returned in API responses.

**It does not prevent secrets from appearing in error messages.** Application code that catches exceptions and logs them may inadvertently log the full database connection string, including embedded credentials. Audit your error handling and logging for credential leakage before relying on Infisical's protections upstream.

The correct mental model is layered defense: Infisical removes the most common and most catastrophic vector (version control exposure) and provides strong control over which systems can access which secrets. The other vectors require complementary controls — code review, logging policy, access control audits — that are independent of the secrets management system.

---

## Part 7: The Migration Path for Projects Already Using .env

If you are running a project with secrets currently stored in `.env` files, here is the migration path that does not require stopping all development:

**Step 1: Audit the current .env files.** List every secret in every `.env` file across every environment. Catalog which services each secret belongs to and which environment (dev, staging, prod) each file corresponds to.

**Step 2: Check repository history for accidental commits.** Run a scan of the git history for any prior accidental commits of `.env` files:

```bash
git log --all --full-history -- "*.env" "*.env.*"

If any .env files appear in the history, rotate all credentials in those files immediately, regardless of when the commit happened. Assume exposure.

Step 3: Set up Infisical and populate secrets. Create your Infisical project, configure the three environments (dev, staging, prod), and add all secrets. Use the Vercel integration to sync if applicable.

Step 4: Update application code. Replace any code that reads from .env files directly with code that reads process.env variables. This is the default pattern for most modern frameworks — if you are already using process.env.DATABASE_URL, no code change is required. The behavior is identical; only the injection mechanism changes.

Step 5: Update developer workflow documentation. The README should document that infisical run --env=dev -- is required to prefix all commands. Add it to the package.json scripts for common operations so developers do not need to type it manually:

{
  "scripts": {
    "dev": "infisical run --env=dev -- next dev",
    "migrate": "infisical run --env=dev -- bun run drizzle-kit migrate",
    "typecheck": "infisical run --env=dev -- tsc --noEmit"
  }
}

Step 6: Delete .env files and update .gitignore. Remove all .env files from the working tree and ensure .gitignore is comprehensive:

# Secrets — never commit
.env
.env.*
.env.local
.env.development
.env.staging
.env.production
*.env

The Infisical .infisical.json file is safe to commit — it contains only the project identifier, not any secrets.


The Cost of Not Doing This

A security breach caused by an exposed credential is not just a direct cost — it is a credibility event. Customers who trusted you with their data have to be notified. Regulatory bodies in the EU (GDPR), UAE (PDPL), and Saudi Arabia (PDPL-SA) have breach notification requirements with timelines measured in hours and days, not weeks. Fines for inadequate data protection are real and growing.

For a startup with zero dedicated security staff, the correct posture is to reduce the attack surface as aggressively as possible at the points that are cheapest to fix. Version control exposure is the cheapest class of credential vulnerability to eliminate — it requires a one-time setup investment of under two hours and ongoing costs of essentially zero.

The alternative — operating with credentials in .env files and hoping no one commits them — is not a risk management strategy. It is a deferred liability. Every month that passes without a breach does not reduce the probability of a future breach. It only adds more credentials to the exposure surface.

If you are building on a stack similar to ODSEA's — Next.js, Supabase, Vercel, with database migrations handled through Drizzle — the full-stack AI development service includes secrets management setup as a standard component of every project delivery. Talk to us about what secure-by-default looks like for your specific stack.

InfisicalSecrets ManagementSecurityDevOpsStartup

Related Articles