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

# App Lifecycle

Every Copper binary starts the same way: you create the app, then hand it something to run. Let's look at the `main.go` from a generated project:

```go cmd/app/main.go theme={null}
func main() {
	app := copper.New()

	server, err := InitServer(app)
	if err != nil {
		log.Fatalln("Failed to init server: ", err.Error())
	}

	app.Start(server)
}
```

`copper.New()` builds the **app container**, which parses command-line flags, loads config, and sets up the logger and lifecycle:

```go theme={null}
type App struct {
	Lifecycle *clifecycle.Lifecycle
	Config    cconfig.Loader
	Logger    clogger.CoreLogger
}
```

### Runners

Anything with a `Run() error` method can be run by the app:

```go theme={null}
type Runner interface {
	Run() error
}
```

`chttp.Server` and `csql.Migrator` both implement it, and you are free to implement it with your own types, such as a job worker, a queue consumer, or a one-off script.

### Run vs. Start

* **`app.Run(runners...)`** is for short-lived work. It runs each runner in order, then shuts down. This is how the migrate binary works: `app.Run(migrator)`.
* **`app.Start(runners...)`** is for long-lived work. It runs each runner, then blocks until the process receives `SIGINT` or `SIGTERM`, and shuts down gracefully. This is how the HTTP server runs.

If any runner returns an error, the app logs it and exits with code 1.

### Registering Cleanup with OnStop

You may register cleanup work, such as closing a database connection or flushing a client, using `OnStop`. Copper will run these functions during shutdown, each with a deadline context:

```go theme={null}
func NewClient(lc *clifecycle.Lifecycle) (*Client, error) {
	conn, err := dial()
	if err != nil {
		return nil, err
	}

	lc.OnStop(func(ctx context.Context) error {
		return conn.Close()
	})

	return &Client{conn: conn}, nil
}
```

### Running Background Goroutines

You should not use a bare `go` statement for long-running work, since the app has no way to wait for it during shutdown. Instead, use `Lifecycle.Go`. Goroutines started this way are tracked, awaited on shutdown, and recovered from panics, so a panic is logged with its stack trace instead of crashing the app:

```go theme={null}
lc.Go(func(ctx context.Context) {
	err := telemetry.Upload(ctx, flightData)
	if err != nil {
		logger.Error("Failed to upload flight data", err)
	}
})
```

### Graceful Shutdown

Shutdown happens in two phases, which is what makes it graceful:

1. **`Lifecycle.Shutdown()`** is a channel that closes when shutdown *begins*. Loops should select on it to exit.
2. **`Lifecycle.Context()`** is a context that stays valid *during* shutdown so that in-flight work can finish. It is only canceled at the very end.

A long-running worker uses both:

```go theme={null}
func (t *Tracker) Run() error {
	t.lc.Go(func(ctx context.Context) {
		ticker := time.NewTicker(10 * time.Second)
		defer ticker.Stop()

		for {
			select {
			case <-t.lc.Shutdown():
				return
			case <-ticker.C:
				t.pingSatellites(ctx)
			}
		}
	})

	return nil
}
```

<Warning>
  Shutdown waits up to 30 seconds for `Go` goroutines to finish before running `OnStop` funcs and exiting. Work that can't finish in time is abandoned, so keep background tasks incremental.
</Warning>
