Opencore
Secure, event-driven TypeScript Framework & Runtime engine (FiveM and RageMP)
Install / Use
/learn @newcore-network/OpencoreREADME
OpenCore Framework - Stable v1
OpenCore is a TypeScript multiplayer runtime framework targeting CitizenFX runtimes (Cfx/RageMP) via adapters.
It is not a gamemode or RP framework. It provides:
- A stable execution model (server and client)
- Dependency Injection and metadata-driven wiring
- An event/command system
- Security primitives (validation, access control, rate limiting)
License: MPL-2.0
Discord Community | Docs | OpenCore CLI
Installation
pnpm add @open-core/framework reflect-metadata tsyringe zod uuid
This framework uses TypeScript decorators. Ensure your project has decorators enabled.
Imports and entry points
The package exposes subpath entry points:
@open-core/framework(root)@open-core/framework/server@open-core/framework/client
Architecture
OpenCore follows a Ports & Adapters (Hexagonal) architecture.
- Kernel (
src/kernel): engine-agnostic infrastructure (DI, logger, metadata scanning) - Runtime (
src/runtime): multiplayer execution model (controllers, processors, security, lifecycle) - Adapters (
src/adapters): platform integration (Cfx, Node testing)
The runtime never auto-detects the platform. Adapters are selected explicitly at bootstrap time.
Cfx game profiles
OpenCore treats CitizenFX (cfx) as the platform and supports game profiles (gta5 and rdr3).
- Shared runtime APIs (events, exports, transport, DI) are registered through the Cfx adapter.
- Game-specific behavior is controlled through platform capabilities/config (
gameProfile,defaultSpawnModel, etc.). - Optional RedM-specific enhancements can be layered as external libraries without changing core runtime contracts.
Operating modes
Each instance runs in exactly one mode configured via Server.init():
CORE: authoritative runtime. Typically provides identity/auth/players via exports.RESOURCE: a normal Cfx resource using CORE as provider for some features.STANDALONE: a self-contained runtime (useful for tooling, simulations, or small servers).
Server bootstrap
Initialize the server runtime:
import { Server } from '@open-core/framework/server'
await Server.init({
mode: 'CORE'
})
Some features require providers (depending on your mode and configuration). Configure them before calling init():
import { Server } from '@open-core/framework/server'
Server.setPrincipalProvider(MyPrincipalProvider)
Server.setSecurityHandler(MySecurityHandler)
Server.setPersistenceProvider(MyPlayerPersistence)
Server.setNetEventSecurityObserver(MyNetEventSecurityObserver)
Controllers and decorators
OpenCore uses a decorator + processor pattern.
Decorators store metadata with Reflect.defineMetadata(). During bootstrap, the MetadataScanner reads metadata and processors register handlers.
Commands
import { Controller, Command, Guard, Throttle, Player } from '@open-core/framework/server'
import { z } from 'zod'
const TransferSchema = z.tuple([z.coerce.number().int().positive(), z.coerce.number().min(1)])
@Controller()
export class BankController {
@Command({
command: 'transfer',
usage: '/transfer <id> <amount>',
schema: TransferSchema,
})
@Guard({ rank: 1 })
@Throttle(1, 2000)
async transfer(player: Player, args: z.infer<typeof TransferSchema>) {
const [targetId, amount] = args
player.emit('chat:message', `transfer -> ${targetId} (${amount})`)
}
}
Network events
@OnNet() handlers always receive Player as the first parameter.
import { Controller, OnNet, Player } from '@open-core/framework/server'
import { z } from 'zod'
const PayloadSchema = z.object({ action: z.string(), amount: z.number().int().positive() })
@Controller()
export class ExampleNetController {
@OnNet('bank:action', { schema: PayloadSchema })
async onBankAction(player: Player, payload: z.infer<typeof PayloadSchema>) {
player.emit('chat:message', `action=${payload.action} amount=${payload.amount}`)
}
}
Security decorators
@Guard({ rank })or@Guard({ permission })@Throttle(limit, windowMs)@RequiresState({ missing: [...] })
Exports
@Export() defines a public resource API. Adapters may expose both direct/local access through getResource() and an optional explicit async helper layer through getRemoteResource() / waitForRemoteResource().
import { Controller, Export } from '@open-core/framework/server'
import { IExports } from '@open-core/framework/contracts/server'
@Controller()
export class DatabaseController {
@Export('pingDatabase')
async pingDatabase() {
return { success: true }
}
}
interface DatabaseExports {
pingDatabase(): Promise<{ success: boolean }>
}
class ExampleConsumer {
constructor(private readonly exportsService: IExports) {}
async ping() {
const database = await this.exportsService.waitForRemoteResource<DatabaseExports>('database', {
exportName: 'pingDatabase',
})
return database.pingDatabase()
}
}
Guidance:
getResource()is for local/synchronous resolution used by framework internals.waitForRemoteResource()/getRemoteResource()are optional adapter utilities for explicit async resource-to-resource calls.
Library events
Use library wrappers to emit domain events and @OnLibraryEvent() to observe them.
@OnLibraryEvent() listens to events emitted through library.emit(...) only.
It does not listen to emitExternal, emitNetExternal, or emitServer.
import { Server } from '@open-core/framework/server'
const characters = Server.createServerLibrary('characters')
@Controller()
export class CharacterListeners {
@OnLibraryEvent('characters', 'session:created')
onSessionCreated(payload: { sessionId: string; playerId: number }) {
// optional listener for library domain events
}
}
characters.emit('session:created', { sessionId: 's-1', playerId: 10 })
Client usage follows the same pattern with Client.createClientLibrary(...) and
@Client.OnLibraryEvent(...).
Plugins
Plugin contracts are exposed by runtime entrypoint, not by root:
- Server plugins:
@open-core/framework/server - Client plugins:
@open-core/framework/client
import { Server, type OpenCorePlugin } from '@open-core/framework/server'
import { Client, type OpenCoreClientPlugin } from '@open-core/framework/client'
const serverPlugin: OpenCorePlugin = {
name: 'server-example',
install(ctx) {
ctx.server.registerApiExtension('ExampleServerDecorator', () => {})
},
}
const clientPlugin: OpenCoreClientPlugin = {
name: 'client-example',
install(ctx) {
ctx.client.registerApiExtension('ExampleClientDecorator', () => {})
},
}
await Server.init({ mode: 'CORE', plugins: [serverPlugin] })
await Client.init({ mode: 'CORE', plugins: [clientPlugin] })
Module augmentation for plugin APIs:
declare module '@open-core/framework/server' {
interface ServerPluginApi {
ExampleServerDecorator: () => void
}
}
declare module '@open-core/framework/client' {
interface ClientPluginApi {
ExampleClientDecorator: () => void
}
}
Testing
Tests run with Vitest.
pnpm test
pnpm test:unit
pnpm test:integration
pnpm test:coverage
Note: pnpm test does not run benchmarks.
Benchmarks
Benchmarks are split by value, so the default run focuses on framework features that matter for real servers.
pnpm bench
pnpm bench:value
pnpm bench:gold
pnpm bench:startup
pnpm bench:diagnostic
pnpm bench:soak
pnpm bench:load
pnpm bench:all
bench/bench:value: value-focused suite. Commands, net events, RPC, lifecycle, ticks, binary path, bootstrap.bench:gold: hot-path load scenarios only.bench:startup: startup and registration cost.bench:diagnostic: internal and low-level synthetic benchmarks.bench:soak: long-running stress scenario.
Snapshot (latest local run)
Use benchmark/reports/ as the source of truth. Results vary by machine and should be compared relatively, not treated as product guarantees.
- Primary benchmark targets:
- full command execution
- full net event handling
- RPC processing
- player lifecycle churn
- tick budget impact
- bootstrap cost
- binary transport cost
Full reports and methodology are available in benchmark/README.md.
Reports
Benchmark reports are generated under benchmark/reports/.
pnpm bench:allgenerates aggregated reports (text/json/html)- Load metrics used by load benchmarks are persisted in
benchmark/reports/.load-metrics.json
For details about the benchmark system, see benchmark/README.md.
Development scripts
pnpm build
pnpm watch
pnpm lint
pnpm lint:fix
pnpm format
License
MPL-2.0. See LICENSE.
Related Skills
node-connect
353.1kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
111.6kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
Writing Hookify Rules
111.6kThis skill should be used when the user asks to "create a hookify rule", "write a hook rule", "configure hookify", "add a hookify rule", or needs guidance on hookify rule syntax and patterns.
review-duplication
100.7kUse this skill during code reviews to proactively investigate the codebase for duplicated functionality, reinvented wheels, or failure to reuse existing project best practices and shared utilities.
