# The core object

Read your config and your plugins from any handler through this.core. Covers what it carries, running work while your bot starts and stops, and the property a plugin becomes.

Your `/ping` handler replied and stopped there. Most handlers need more than that. They read your own config, or a database, or the rate limiter that a cooldown charges. seedcord builds one object holding all of it while your bot starts, and every handler reaches that object as `this.core`.

```ts title="src/handlers/Ping.ts"
import { SlashHandler, SlashRoute } from '@seedcord/gateway';

@SlashRoute('ping')
export class Ping extends SlashHandler<'ping'> {
    public async execute(): Promise<void> {
        const owners = this.core.config.ownerIds;

        await this.reply(
            `${owners?.length ?? 0} owners configured.`
        );
    }
}
```

The rest of your code reaches the same object. A gate reads it as `ctx.core`. A subscriber and a plugin both read `this.core`, the same as a handler.

## What it carries

{/* prettier-ignore-start */}

| member          | holds                                                                           |
| --------------- | ------------------------------------------------------------------------------- |
| `config`        | the object you passed to `new Seedcord()`                                       |
| `rest`          | Discord's REST client, which is discord.js's own on gateway                     |
| `applicationId` | your bot's Discord application id                                               |
| `rateLimiter`   | the windows behind [`Cooldown`](/checks/cooldown), and yours to charge directly |
| `bus`           | [publishing and subscribing](/events/bus) for framework and application events  |
| `startup`       | `addTask`, for work that runs while your bot boots                              |
| `shutdown`      | `addTask`, for work that runs while it stops                                    |
| `bot`           | the discord.js client and your bot token. Gateway only                          |

{/* prettier-ignore-end */}

[`CoreBase`](https://docs.seedcord.org/packages/core/latest/core-base) is the part both transports share. Each one adds its own members to it.

`config` reaches your handler exactly as you wrote it in `src/bot.ts`. `ownerIds` is optional. That's why the sample above guards it. ([`OwnerOnly`](https://docs.seedcord.org/packages/core/latest/owner-only) reads that same key by the way)

## Running work while your bot starts and stops

Suppose your bot opens a cache of its own. Something has to fill it before the first command runs, then close it again when the bot stops. Doing that yourself means racing your own startup on one end and wiring up `SIGTERM` on the other.

[`startup.addTask`](https://docs.seedcord.org/packages/core/latest/coordinated-startup#add-task) and [`shutdown.addTask`](https://docs.seedcord.org/packages/core/latest/coordinated-shutdown#add-task) put that work in a phase, and seedcord runs the phases in order. Although, it's better to write a small [plugin](/plugins/) for yourself instead. It provides hooks for startup and shutdown tasks, timeouts, and more, keeping your initialization and cleanup logic organized and consistent with the framework's lifecycle.

```ts title="src/bot.ts"
import { resolve } from 'node:path';

import {
    Seedcord,
    ShutdownPhase,
    StartupPhase
} from '@seedcord/gateway';
import { GatewayIntentBits } from 'discord.js';

export const seedcord = new Seedcord({
    bot: {
        clientOptions: { intents: [GatewayIntentBits.Guilds] },
        interactions: {
            path: resolve(import.meta.dirname, './handlers')
        },
        commands: {
            path: resolve(import.meta.dirname, './commands')
        },
        events: { path: null }
    },
    subscribers: { path: null }
});

seedcord.startup.addTask(
    StartupPhase.Configuration,
    'open-cache',
    openCache
);

seedcord.shutdown.addTask(
    ShutdownPhase.Disconnect,
    'close-cache',
    closeCache
);

export default seedcord;
```

Each call takes the phase, a name for the logs, the work itself, and an optional timeout in milliseconds. A startup task gets 10000 and a shutdown task gets 5000 unless you pass your own.

Pick the phase by what has to be true when your task runs. The sample fills its cache in `Configuration`, before anything connects, and closes it in `Disconnect`, the phase for closing external resources. [Plugin lifecycle](/plugins/lifecycle) has a table of every startup and shutdown phase, with what has already happened by each one.

Tasks in the same phase run in parallel, including seedcord's own. The next phase starts once all of them have finished. If one task needs another task's result, put it in a later phase.

> **Gateway only**
>
> `core.applicationId` comes from the discord.js client, so it resolves during the Login phase. Reading it from a `Configuration` task throws. Every handler runs long after login, which makes it safe there.

> **Http only**
>
> `createSeedcord` builds a core without these phases, since its build never runs startup or shutdown at all. Both `addTask` calls still compile on an edge bot, though they throw `CoreLifecycleUnavailable` when they run. Do that work inside your handler.

## A plugin becomes a property

Constructing a plugin yourself leaves you passing it down through imports and ordering its setup by hand. Attaching it puts it on the core, where every handler already reaches.

[`attach()`](https://docs.seedcord.org/packages/gateway/latest/seedcord#attach) takes a key, a plugin class, and whatever that plugin's constructor needs after the host. The key becomes a property on the same object your handlers read.

```ts
import { resolve } from 'node:path';

import { Seedcord } from '@seedcord/gateway';
import { KyselyPostgres } from '@seedcord/plugin-kysely-postgres';
import { GatewayIntentBits } from 'discord.js';

seedcord.attach('db', KyselyPostgres, {
    dir: resolve(import.meta.dirname, './services'),
    connectionString: 'postgres://localhost:5432/seedcord',
    migrations: {
        path: resolve(import.meta.dirname, './migrations')
    }
});
```

`attach()` sets the property at runtime. `seedcord codegen` then writes the type that makes `this.core.db` compile inside a handler.

Every tab ahead uses `this.core` somewhere.
