> ## Documentation Index
> Fetch the complete documentation index at: https://docs.opensource-together.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Run the app locally

> Get OpenSource Together running on your machine in two commands: no backend, no database, no credentials

## Two commands

```bash theme={null}
git clone https://github.com/opensource-together/opensource-together.git
cd opensource-together
pnpm install
pnpm dev:mock
```

Open [http://localhost:3000](http://localhost:3000). That's the whole setup.

<Success>
  **No `.env` file. No database. No Docker. No OAuth app. No backend access.** `pnpm dev:mock`
  starts a local mock of the API alongside the web app, seeded with realistic project data.
</Success>

## Prerequisites

<CardGroup cols={2}>
  <Card title="Node.js 22+" icon="node-js">
    The repo requires `node >= 22`. [Download Node.js](https://nodejs.org/)
  </Card>

  <Card title="pnpm 10" icon="box">
    ```bash theme={null}
    npm install -g pnpm
    ```

    CI builds with pnpm 10. Other package managers aren't supported.
  </Card>
</CardGroup>

## What `pnpm dev:mock` starts

It runs two processes side by side and stops both if either one fails:

| Process | Port                          | What it is                                  |
| ------- | ----------------------------- | ------------------------------------------- |
| `web`   | [3000](http://localhost:3000) | The Next.js app, in dev mode with Turbopack |
| `api`   | 4000                          | A local mock of the OpenSource Together API |

The mock is a real HTTP server: an Express app serving [MSW](https://mswjs.io/) handlers, not
a browser service worker. Server components, `generateMetadata`, and the sitemap all talk to it
too, because the script points both `NEXT_PUBLIC_API_URL` and `INTERNAL_SERVER_API_URL` at
`localhost:4000`.

<Note>
  The script sets every environment variable it needs inline, so **you do not need to copy
  `.env.example`**. Only the optional feature-request form and S3 metadata assets read
  variables the mock doesn't provide.
</Note>

## Signing in without OAuth

You're signed in by default: the mock API issues the same session cookie the real backend uses,
so protected routes like `/dashboard/my-projects` and `/projects/create` just work.

To test signed-out states, use the **Mock mode** button in the **bottom-left corner** of the
page. It flips between *Signed in* and *Signed out* and reloads.

<Warning>
  Mock data lives in memory. Projects you create, profile edits, and uploaded images all
  disappear when you restart the mock server. That's intentional, so every contributor starts
  from the same state.
</Warning>

## Verify your setup

<Steps>
  <Step title="The homepage lists projects">
    [http://localhost:3000](http://localhost:3000) should show a grid of seeded open-source
    projects with working filters and search.
  </Step>

  <Step title="A protected route loads">
    Visit [http://localhost:3000/dashboard/my-projects](http://localhost:3000/dashboard/my-projects).
    You should see projects owned by the mock user, not a redirect to the login page.
  </Step>

  <Step title="The mock API answers">
    ```bash theme={null}
    curl http://localhost:4000/categories
    ```

    Returns a `{ "data": [...], "timestamp": "..." }` payload.
  </Step>

  <Step title="The test suite passes">
    ```bash theme={null}
    pnpm test:mock
    ```
  </Step>
</Steps>

## Common issues

<AccordionGroup>
  <Accordion title="Port 3000 or 4000 is already in use">
    If **3000** is taken, Next.js just picks the next free port and tells you which one in the
    terminal. Nothing to do.

    If **4000** is taken, the mock API can't start. Free that port and run `pnpm dev:mock`
    again.
  </Accordion>

  <Accordion title="A request returns 501">
    The mock API returns `501` for any route it doesn't handle, and logs
    `[mock-api] UNHANDLED <method> <path>` in the terminal. It means the flow you're building
    needs a handler that doesn't exist yet. See [adding a handler](/web-app/mock-api).
  </Accordion>

  <Accordion title="`pnpm install` fails on the lockfile">
    CI installs with `--frozen-lockfile`. If you changed dependencies, commit the updated
    `pnpm-lock.yaml` along with your `package.json` change.
  </Accordion>

  <Accordion title="I want to run against the real API">
    `pnpm dev` (without `:mock`) expects the API at `http://localhost:4000` and requires access
    to the private backend. Contributors should use `pnpm dev:mock`.
  </Accordion>
</AccordionGroup>

## Next steps

<CardGroup cols={2}>
  <Card title="How to contribute" icon="code-branch" href="/contributing/contributing">
    Branches, commits, and what CI will check
  </Card>

  <Card title="The codebase" icon="sitemap" href="/web-app/architecture">
    How features, the data layer, and conventions fit together
  </Card>

  <Card title="Mock API guide" icon="plug" href="/web-app/mock-api">
    Add handlers and seed data for new flows
  </Card>

  <Card title="Web app setup & scripts" icon="terminal" href="/web-app/quickstart">
    Every script, env var, and tooling detail
  </Card>
</CardGroup>
