Show HN: I've been building an ERP for manufacturing for the last 3 years

Aug 5, 2025 - 02:00
 0  0
Show HN: I've been building an ERP for manufacturing for the last 3 years

The open-source operating system for manufacturing

Discord · Website · Issues

Does the world need another ERP?

We built Carbon after years of building end-to-end manufacturing systems with off-the-shelf solutions. We realized that:

  • Modern, API-first tooling didn't exist
  • Vendor lock-in bordered on extortion
  • There is no "perfect ERP" because each company is unique

We built Carbon to solve these problems ☝️.

Architecture

Carbon is designed to make it easy for you to extend the platform by building your own apps through our API. We provide some examples to get you started in the examples folder.

Features:

Technical highlights:

Techstack

Codebase

The monorepo follows the Turborepo convention of grouping packages into one of two folders.

  1. /apps for applications
  2. /packages for shared code

/apps

Package Name Description Local Command
erp ERP Application npm run dev
mes MES npm run dev:mes
academy Academy npm run dev:academy
starter Starter npm run dev:starter

/packages

Package Name Description
eslint-config-carbon Shared, extendable eslint configuration for apps and packages
@carbon/database Database schema, migrations and types
@carbon/documents Transactional PDFs and email templates
@carbon/integrations Integration definitions and configurations
@carbon/jest Jest preset configuration shared across apps and packages
@carbon/jobs Background jobs and workers
@carbon/logger Shared logger used across apps
@carbon/react Shared web-based UI components
@carbon/kv Redis cache client
@carbon/lib Third-party client libraries (slack, resend)
@carbon/stripe Stripe integration
@carbon/tsconfig Shared, extendable tsconfig configuration used across apps and packages
@carbon/utils Shared utility functions used across apps and packages

Development

Setup

  1. Clone the repo into a public GitHub repository (or fork https://github.com/crbnos/carbon/fork). If you plan to distribute the code, keep the source code public to comply with AGPLv3. To clone in a private repository, acquire a commercial license

    git clone https://github.com/crbnos/carbon.git
  2. Go to the project folder

    cd carbon

Make sure that you have Docker installed on your system since this monorepo uses the Docker for local development.

In addition you must configure the following external services:

Service Purpose URL
Upstash Serverless Redis https://console.upstash.com/login
Trigger.dev Job runner https://cloud.trigger.dev/login
Posthog Product analytics platform https://us.posthog.com/signup

Each of these services has a free tier which should be plenty to support local development. If you're self hosting, and you don't want to use Upstash or Posthog, it's pretty easy to replace upstash with a redis container in @carbon/kv and remove the Posthog analytics.

Installation

First download and initialize the repository dependencies.

$ nvm use           # use node v20
$ npm install       # install dependencies
$ npm run db:start  # pull and run the containers

Create an .env file and copy the contents of .env.example file into it

$ cp ./.env.example ./.env
  1. Use the output of npm run db:start to set the supabase entries:
  • SUPABASE_SERVICE_ROLE_KEY=[service_role key]
  • SUPABASE_ANON_KEY=[anon key]
  1. Create a Redis database in upstash and copy the following from the REST API section:
  • UPSTASH_REDIS_REST_URL=[UPSTASH_REDIS_REST_URL]
  • UPSTASH_REDIS_REST_TOKEN=[UPSTASH_REDIS_REST_TOKEN]
  1. Navigate to the project you created in https://cloud.trigger.dev/ and copy the following from the Environments & API Keys section:
  • TRIGGER_PUBLIC_API_KEY=[Public 'dev' API Key, starting 'pk_dev*']
  • TRIGGER_API_KEY=[Server 'dev' API Key, starting 'tr_dev*']
  1. In Posthog go to https://[region].posthog.com/project/[project-id]/settings/project-details to find your Project ID and Project API key:
  • POSTHOG_API_HOST=[https://[region].posthog.com]
  • POSTHOG_PROJECT_PUBLIC_KEY=[Project API Key starting 'phc*']
  1. Add a STRIPE_SECRET_KEY from the Stripe admin interface, and then run npm run -w @carbon/stripe register:stripe to get a STRIP_WEBHOOK_SECRET
  • STRIPE_SECRET_KEY="sk_test_*************"
  • STRIP_WEBHOOK_SECRET="whsec_************"

Then you can run the following:

$ npm run db:build     # run db migrations and seed script
$ npm run build        # build the packages

Finally, start the apps and packages:

$ npm run dev
$ npm run dev:mes        # npm run dev in all apps & packages

You can now sign in with:

username: [email protected] password: carbon

After installation you should be able run the apps locally.

Application URL
ERP http://localhost:3000
MES http://localhost:3001
Academy http://localhost:4111
Starter http://localhost:4000
Postgres postgresql://postgres:postgres@localhost:54322/postgres
Supabase Studio http://localhost:54323/project/default
Mailpit http://localhost:54324
Edge Functions http://localhost:54321/functions/v1/

Notes

To kill the database containers in a non-recoverable way, you can run:

$ npm run db:kill   # stop and delete all database containers

To restart and reseed the database, you can run:

$ npm run db:build # runs db:kill, db:start, and setup

To run a particular application, use the -w workspace flag.

For example, to run test command in the @carbon/react package you can run:

$ npm run test -w @carbon/react

API

The API documentation is located in the ERP app at ${ERP}/x/api/js/intro. It is auto-generated based on changes to the database.

There are two ways to use the API:

  1. From another codebase using a supabase client library:
  1. From within the codebase using our packages.

From another Codebase

Navigate to settings in the ERP to generate an API key. If you're self-hosting you can also use the supabase service key instead of the public key for root access. In that case you don't needto include the carbon-key header.

import { Database } from "@carbon/database";
import { createClient } from "@supabase/supabase-js";

const apiKey = process.env.CARBON_API_KEY;
const apiUrl = process.env.CARBON_API_URL;
const publicKey = process.env.CARBON_PUBLIC_KEY;

const carbon = createClient<Database>(apiUrl, publicKey, {
  global: {
    headers: {
      "carbon-key": apiKey,
    },
  },
});

// returns items from the company associated with the api key
const { data, error } = await carbon.from("item").select("*");

From the Monorepo

import { getCarbonServiceRole } from "@carbon/auth";
const carbon = getCarbonServiceRole();

// returns all items across companies
const { data, error } = await carbon.from("item").select("*");

// returns items from a specific company
const companyId = "xyz";
const { data, error } = await carbon
  .from("item")
  .select("*")
  .eq("companyId", companyId);

What's Your Reaction?

Like Like 0
Dislike Dislike 0
Love Love 0
Funny Funny 0
Angry Angry 0
Sad Sad 0
Wow Wow 0