Get the FREE Ultimate OpenClaw Setup Guide →

turborepo-caching

Flagged

{"isSafe":false,"isSuspicious":true,"riskLevel":"high","findings":[{"category":"system_harm","severity":"high","description":"Self-hosted remote cache (Template 4) uses an unvalidated request parameter (hash) to build file system paths. This can enable path traversal to read/write arbitrary files outside the cache directory, potentially exposing sensitive data or overwriting files.","evidence":"const filePath = join(CACHE_DIR, team, hash);"},{"category":"system_harm","severity":"high","description":"PUT endpoint streams the entire request body to disk without size limits or validation. An attacker could fill disk space or overwrite unintended files via crafted uploads.","evidence":"const stream = createWriteStream(filePath); req.pipe(stream);"},{"category":"data_exfiltration","severity":"high","description":"GET/PUT/HEAD endpoints expose artifact data without authentication. Unauthorized access could leak artifacts or allow tampering with cached content.","evidence":"app.get(\"/v8/artifacts/:hash\", ...); app.put(\"/v8/artifacts/:hash\", ...); app.head(\"/v8/artifacts/:hash\", ...);"},{"category":"data_exfiltration","severity":"high","description":"Artifact cache is accessible by potentially anyone who can reach the server (no auth checks shown). Could enable data leakage or manipulation of cached artifacts.","evidence":"app.get(\"/v8/artifacts/:hash\", async (req, res) => { ... }); app.put(\"/v8/artifacts/:hash\", async (req, res) => { ... }); app.head(\"/v8/artifacts/:hash\", async (req, res) => { ... });"},{"category":"other","severity":"low","description":"Code quality/security note: The code imports createReadStream and createWriteStream from 'fs' but references fs.access in the HEAD handler without importing/using 'fs' as a module. This would cause runtime errors and indicates refactoring gaps.","evidence":"import { createReadStream, createWriteStream } from \"fs\"; ... await fs.access(filePath);"}],"summary":"The skill content is generally safe as a caching/configuration guide, but Template 4 introduces a security risk: unvalidated path components allow path traversal to read/write arbitrary files and the endpoints expose artifacts without authentication. Additionally, there is a code quality issue (fs not imported) that could cause runtime errors. Recommendations: sanitize and validate the hash/path, resolve to an allowed base path and verify it stays under CACHE_DIR, require authentication/authorization for artifact endpoints, implement upload size limits and proper error handling, and fix the missing/fs import to avoid runtime failures."}

npx machina-cli add skill wshobson/agents/turborepo-caching --openclaw
Files (1)
SKILL.md
8.1 KB

Turborepo Caching

Production patterns for Turborepo build optimization.

When to Use This Skill

  • Setting up new Turborepo projects
  • Configuring build pipelines
  • Implementing remote caching
  • Optimizing CI/CD performance
  • Migrating from other monorepo tools
  • Debugging cache misses

Core Concepts

1. Turborepo Architecture

Workspace Root/
├── apps/
│   ├── web/
│   │   └── package.json
│   └── docs/
│       └── package.json
├── packages/
│   ├── ui/
│   │   └── package.json
│   └── config/
│       └── package.json
├── turbo.json
└── package.json

2. Pipeline Concepts

ConceptDescription
dependsOnTasks that must complete first
cacheWhether to cache outputs
outputsFiles to cache
inputsFiles that affect cache key
persistentLong-running tasks (dev servers)

Templates

Template 1: turbo.json Configuration

{
  "$schema": "https://turbo.build/schema.json",
  "globalDependencies": [".env", ".env.local"],
  "globalEnv": ["NODE_ENV", "VERCEL_URL"],
  "pipeline": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": ["dist/**", ".next/**", "!.next/cache/**"],
      "env": ["API_URL", "NEXT_PUBLIC_*"]
    },
    "test": {
      "dependsOn": ["build"],
      "outputs": ["coverage/**"],
      "inputs": ["src/**/*.tsx", "src/**/*.ts", "test/**/*.ts"]
    },
    "lint": {
      "outputs": [],
      "cache": true
    },
    "typecheck": {
      "dependsOn": ["^build"],
      "outputs": []
    },
    "dev": {
      "cache": false,
      "persistent": true
    },
    "clean": {
      "cache": false
    }
  }
}

Template 2: Package-Specific Pipeline

// apps/web/turbo.json
{
  "$schema": "https://turbo.build/schema.json",
  "extends": ["//"],
  "pipeline": {
    "build": {
      "outputs": [".next/**", "!.next/cache/**"],
      "env": ["NEXT_PUBLIC_API_URL", "NEXT_PUBLIC_ANALYTICS_ID"]
    },
    "test": {
      "outputs": ["coverage/**"],
      "inputs": ["src/**", "tests/**", "jest.config.js"]
    }
  }
}

Template 3: Remote Caching with Vercel

# Login to Vercel
npx turbo login

# Link to Vercel project
npx turbo link

# Run with remote cache
turbo build --remote-only

# CI environment variables
TURBO_TOKEN=your-token
TURBO_TEAM=your-team
# .github/workflows/ci.yml
name: CI

on:
  push:
    branches: [main]
  pull_request:

env:
  TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
  TURBO_TEAM: ${{ vars.TURBO_TEAM }}

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: "npm"

      - name: Install dependencies
        run: npm ci

      - name: Build
        run: npx turbo build --filter='...[origin/main]'

      - name: Test
        run: npx turbo test --filter='...[origin/main]'

Template 4: Self-Hosted Remote Cache

// Custom remote cache server (Express)
import express from "express";
import { createReadStream, createWriteStream } from "fs";
import { mkdir } from "fs/promises";
import { join } from "path";

const app = express();
const CACHE_DIR = "./cache";

// Get artifact
app.get("/v8/artifacts/:hash", async (req, res) => {
  const { hash } = req.params;
  const team = req.query.teamId || "default";
  const filePath = join(CACHE_DIR, team, hash);

  try {
    const stream = createReadStream(filePath);
    stream.pipe(res);
  } catch {
    res.status(404).send("Not found");
  }
});

// Put artifact
app.put("/v8/artifacts/:hash", async (req, res) => {
  const { hash } = req.params;
  const team = req.query.teamId || "default";
  const dir = join(CACHE_DIR, team);
  const filePath = join(dir, hash);

  await mkdir(dir, { recursive: true });

  const stream = createWriteStream(filePath);
  req.pipe(stream);

  stream.on("finish", () => {
    res.json({
      urls: [`${req.protocol}://${req.get("host")}/v8/artifacts/${hash}`],
    });
  });
});

// Check artifact exists
app.head("/v8/artifacts/:hash", async (req, res) => {
  const { hash } = req.params;
  const team = req.query.teamId || "default";
  const filePath = join(CACHE_DIR, team, hash);

  try {
    await fs.access(filePath);
    res.status(200).end();
  } catch {
    res.status(404).end();
  }
});

app.listen(3000);
// turbo.json for self-hosted cache
{
  "remoteCache": {
    "signature": false
  }
}
# Use self-hosted cache
turbo build --api="http://localhost:3000" --token="my-token" --team="my-team"

Template 5: Filtering and Scoping

# Build specific package
turbo build --filter=@myorg/web

# Build package and its dependencies
turbo build --filter=@myorg/web...

# Build package and its dependents
turbo build --filter=...@myorg/ui

# Build changed packages since main
turbo build --filter='...[origin/main]'

# Build packages in directory
turbo build --filter='./apps/*'

# Combine filters
turbo build --filter=@myorg/web --filter=@myorg/docs

# Exclude package
turbo build --filter='!@myorg/docs'

# Include dependencies of changed
turbo build --filter='...[HEAD^1]...'

Template 6: Advanced Pipeline Configuration

{
  "$schema": "https://turbo.build/schema.json",
  "pipeline": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": ["dist/**"],
      "inputs": ["$TURBO_DEFAULT$", "!**/*.md", "!**/*.test.*"]
    },
    "test": {
      "dependsOn": ["^build"],
      "outputs": ["coverage/**"],
      "inputs": ["src/**", "tests/**", "*.config.*"],
      "env": ["CI", "NODE_ENV"]
    },
    "test:e2e": {
      "dependsOn": ["build"],
      "outputs": [],
      "cache": false
    },
    "deploy": {
      "dependsOn": ["build", "test", "lint"],
      "outputs": [],
      "cache": false
    },
    "db:generate": {
      "cache": false
    },
    "db:push": {
      "cache": false,
      "dependsOn": ["db:generate"]
    },
    "@myorg/web#build": {
      "dependsOn": ["^build", "@myorg/db#db:generate"],
      "outputs": [".next/**"],
      "env": ["NEXT_PUBLIC_*"]
    }
  }
}

Template 7: Root package.json Setup

{
  "name": "my-turborepo",
  "private": true,
  "workspaces": ["apps/*", "packages/*"],
  "scripts": {
    "build": "turbo build",
    "dev": "turbo dev",
    "lint": "turbo lint",
    "test": "turbo test",
    "clean": "turbo clean && rm -rf node_modules",
    "format": "prettier --write \"**/*.{ts,tsx,md}\"",
    "changeset": "changeset",
    "version-packages": "changeset version",
    "release": "turbo build --filter=./packages/* && changeset publish"
  },
  "devDependencies": {
    "turbo": "^1.10.0",
    "prettier": "^3.0.0",
    "@changesets/cli": "^2.26.0"
  },
  "packageManager": "npm@10.0.0"
}

Debugging Cache

# Dry run to see what would run
turbo build --dry-run

# Verbose output with hashes
turbo build --verbosity=2

# Show task graph
turbo build --graph

# Force no cache
turbo build --force

# Show cache status
turbo build --summarize

# Debug specific task
TURBO_LOG_VERBOSITY=debug turbo build --filter=@myorg/web

Best Practices

Do's

  • Define explicit inputs - Avoid cache invalidation
  • Use workspace protocol - "@myorg/ui": "workspace:*"
  • Enable remote caching - Share across CI and local
  • Filter in CI - Build only affected packages
  • Cache build outputs - Not source files

Don'ts

  • Don't cache dev servers - Use persistent: true
  • Don't include secrets in env - Use runtime env vars
  • Don't ignore dependsOn - Causes race conditions
  • Don't over-filter - May miss dependencies

Resources

Source

git clone https://github.com/wshobson/agents/blob/main/plugins/developer-essentials/skills/turborepo-caching/SKILL.mdView on GitHub

Overview

This skill teaches configuring Turborepo for fast monorepo builds using local and remote caches. It covers architecture, core pipeline concepts, and practical templates to implement caching effectively across development and CI/CD. Use it to accelerate workflows, reduce re-builds, and enable distributed caching.

How This Skill Works

Turborepo caching is driven by a turbo.json pipeline that declares dependsOn, cache, outputs, inputs, and persistent settings. The templates demonstrate local and remote caching setups (including Vercel remote caching and a self-hosted server) to share artifacts across teams and environments. By aligning cache keys with inputs and specifying outputs, Turborepo reuses previous work and speeds up subsequent runs.

When to Use It

  • Setting up new Turborepo projects
  • Configuring build pipelines
  • Implementing remote caching
  • Optimizing CI/CD performance
  • Debugging cache misses

Quick Start

  1. Step 1: Create a turbo.json configuration (Template 1) with your pipeline and outputs.
  2. Step 2: Add inputs/outputs and enable caching for stable tasks (lint, typecheck) as needed.
  3. Step 3: Set up remote caching (Template 3 or Template 4), login/link, and configure CI with TURBO_TOKEN.

Best Practices

  • Start with a minimal turbo.json and iteratively add outputs, inputs, and dependencies to refine the cache.
  • Use globalDependencies and globalEnv to ensure cache consistency across environments.
  • Enable cache for stable tasks (lint, typecheck) and be cautious with dev servers (persistent pipelines).
  • Integrate remote caching (e.g., Vercel or self-hosted) in CI to reduce runner load and build times.
  • Regularly monitor cache hits/misses and invalidate caches when underlying artifacts change.

Example Use Cases

  • A multi-app monorepo (apps/web, apps/docs, packages/ui, packages/config) configuring turbo.json to optimize builds.
  • Migrating from another tool (e.g., Lerna) to Turborepo and enabling remote caching to speed up migrations.
  • GitHub Actions CI workflow using TURBO_TOKEN and TURBO_TEAM to enable remote caching for all jobs.
  • Frontend projects using remote cache with Vercel to share artifacts across teams and environments.
  • Self-hosted remote cache with an Express server to.publish and retrieve artifacts across the organization.

Frequently Asked Questions

Add this skill to your agents
Sponsor this space

Reach thousands of developers