Obsi Lib can run on a framework or inventory it does not know, and you can replace a single function of a supported one.
Both happen in the `editable/` folder, so library updates never overwrite them.

## A custom framework

:::::steps
### Switch to custom

```lua title="editable/shared/config.lua"
ObsiConfig.Framework = 'custom'
```

### Fill in the functions

Write your functions into `ObsiEditable.framework` in `editable/server/framework.lua` and `editable/client/framework.lua`.

::::hint{type="info" title="Everything is optional"}
Every function is optional.
A missing one falls back to a safe default.
::::
:::::

## Patching one function

To change a single behaviour on a supported framework, put a function with the same name in `ObsiEditable.frameworkPatch`:

```lua title="editable/server/framework.lua"
ObsiEditable.frameworkPatch = {
    gender = function(src)
        return MyIdentity.getGender(src)
    end,
}
```

Everything else keeps using the detected framework.

## A custom inventory

Inventories work the same way:

| | Framework | Inventory |
| --- | --- | --- |
| Config | `ObsiConfig.Framework = 'custom'` | `ObsiConfig.Inventory = 'custom'` |
| Full replacement | `ObsiEditable.framework` | `ObsiEditable.inventory` |
| Single-function patch | `ObsiEditable.frameworkPatch` | `ObsiEditable.inventoryPatch` |

Returning `nil` from a custom inventory function falls back to the framework's own item functions.
The stash hooks (`registerStash`, `openStash`, `stashAdd`, `stashRemove`, `stashItems`) are optional.

## A custom notification system

Set `ObsiConfig.Notify = 'custom'` and write the backend in `editable/server/notify.lua` and `editable/client/notify.lua`.
