โ† Core
In the workslicense: BSD-3-Clause

tom_core_kernel

tom_core_kernel ยท v1.1.16

The foundation every other Tom layer is composed from: dependency injection (bean locator), observable state, security and the four-state authorization model, resource resolution, logging, JSON, isolate pooling, settings and runtime utilities. Reflection-aware, with no UI or server-runtime dependency, so it runs unchanged on CLI, server and Flutter targets.

See License
Status
In the works
LOC
14.5k
Tests
3.8k
Test LOC
539.1k

Overview

From the module readme.md file:

What it enables

Enables Shared state, security and resources, Runtime auto-assembly via reflection, Any Dart target.

Relationships

Standalone โ€” no declared relationships.

tom_core_kernel

> Attribution. The Tom Framework is developed by Peter Nicolai Alexis Kyaw. > These packages are BSD-3-Clause licensed and internal (publish_to: none).

> โš ๏ธ Work in progress โ€” unstable API. The Tom Core packages are under active > development and not yet stable. Their version numbers carry no promise about > API maturity: they are publishing counters, not semver. The patch number is > bumped on every change โ€” including breaking changes โ€” so a patch bump may > break you. Expect the public surface to shift without notice. Not recommended > for production use yet.

Core kernel library for the Tom Framework โ€” the platform-neutral building blocks every other Tom layer is composed from.

Overview

tom_core_kernel is the foundation of the Tom Framework. It carries no UI and no server runtime, so it runs unchanged on any Dart target โ€” CLI, server, or Flutter. Everything layered above it (the server stack, the Flutter / UI layers) depends on the contracts defined here.

The kernel is reflection-aware: it builds on tom_reflection so observable objects, security principals, and resource trees can be introspected and serialized without hand-written boilerplate. Conceptually it provides five things:

  • Observable state โ€” values and objects that notify observers on change, with

a terse operator syntax (| set, ~ read, >> observe). - Ambient context โ€” zone-propagated execution context and hierarchical scope identifiers, so a deeply nested call can resolve "where am I" without threading parameters through every frame. - Dependency injection โ€” a reflection-driven bean container. - Security โ€” authentication, a layered access-control model, bearer-token auth, and a four-state authorization model (TomAuthState). - Runtime utilities โ€” logging (including isolate/remote), an isolate worker pool, a durable job scheduler, the canonical success-or-error result envelope, JSON tree helpers, resource providers, settings, timezone-aware timestamps, and ordered shutdown.

Installation

This package is internal to the Tom workspace and consumed by path (it is publish_to: none), so there is no dart pub add line. Add it as a path dependency:

dependencies:
  tom_core_kernel:
    path: ../tom_core_kernel

Then import the single barrel:

import 'package:tom_core_kernel/tom_core_kernel.dart';

The barrel re-exports tom_basics, tom_crypto and tom_reflection, so one import covers the common surface. SDK: Dart ^3.10.0. Modules that use reflection require a dart run build_runner build to generate *.reflection.dart.

Features

Observable state (observable)

TypeResponsibility
TomObject<T>Base observable holding a single typed value
TomString / TomInt / TomDouble / TomBool Typed scalar observables
TomClass / TomList / TomMap Observable composites

Context & scope (context, scope_ids)

TypeResponsibility
TomExecutionContextAmbient, zone-propagated execution context
TomContextEntry<T>A typed value carried on the context
TomScope / ScopeCapture Hierarchical, zone-propagated scope identifiers

Dependency injection (beanlocator)

TypeResponsibility
TomComponent The discovery annotation, carrying the interface Type the annotated class registers under
TomBean<T> Typed handle that resolves its bean lazily, by type or by name
TomNoInterface The Type @tomComponent carries, for a bean registered under its own type

initializeBeanContext() runs the discovery pass: it finds the @TomComponent-annotated classes, selects one implementation per interface for the active environment and platform, instantiates it, and then resolves every handle declared so far so a missing binding fails at start-up.

Security (security)

TypeResponsibility
TomAuthorization / TomAuthState Authorization decisions and the four-state model
TomAccessControl (+ TomNoAccess , TomPublicAccess , TomAuthenticatedAccess , TomGuestAccess ) Layered access-control rules
TomBearerAuthenticationBearer-token authentication
TomAccessControlInformation , TomGroup , TomRole , TomEntitlement Principal / ACI model
TomResourceRoleRegistry, TomResourceRoleAdapter Per-principal-per-resource roles โ€” the same user as owner of one resource and viewer of another. The kernel's only shipped TomAuthorizationAdapter
tomOAuthApi , TomOAuthProviderDto , TomOAuthAuthorizationRequest , TomOAuthCallbackRequest , TomOAuthApiService The wire contract for external-identity login ("Sign in with โ€ฆ"). The /oauth/complete endpoint โ€” which the application calls, forwarding what the provider's redirect left on its own page โ€” answers with TomAuthenticationResult โ€” the same type /login answers with, because an OAuth provider resolves a principal and everything after that is the one login path every credential reaches, second factor included.

Runtime utilities

ModuleKey typesWhat it does
logging isolate & remote log output/server Structured logging across isolates and the wire
isolate_pooling TomWorker , TomWorkerPool , TomExecutor , TomCommand Worker-pool execution across isolates
scheduling TomScheduler , TomJobDefinition , TomSchedule (cron / calendar / interval / once / event), TomJobStore , TomLeaseLock Durable, leased job scheduling with retry and catch-up policies
json map-merge, pretty-print helpers JSON tree merge/serialization utilities
resources TomTextResourceProvider, TomConfigResourceProvider Text and configuration resource resolution
settingsclient settings/authorizationSettings management
preferences TomUserPreferenceKeys , TomUserPreferenceDto , TomUserPreferenceCodec , tomUserPreferencesApi The contract both preference stores bind to: well-known keys, wire DTOs, the value codec, and the /preferences API
shutdown_cleanup TomShutdownCleanup, TomDisposable, TomClosable Ordered shutdown / cleanup hooks
timezoned DateTimestamp, timezone tables Timezone-aware date/time
http_connection server connection, endpoints, headers HTTP connection helpers
little_things zone helpers, formatting, TomException Small cross-cutting utilities
reflection reflection bootstrap Reflection support built on tom_reflection

Quick start

Observable state with automatic observer propagation:

import 'package:tom_core_kernel/tom_core_kernel.dart';

void main() {
  final count = TomInt(0);
  final observer = count >> (obs) => print('changed to ${~count}');
  count | 5; // prints: changed to 5
  count.removeObserver(observer);
}

| sets the value, ~ reads it, and >> registers an observer that fires on every change. Observers are held by weak reference, so keep the one >> returns โ€” an observer nothing references is collected and silently stops firing. See doc/observable/observable.md.

Example projects

Every module ships a runnable sample folder under example/samples/, and the top-level run_all_samples.dart runs the whole set in sequence. That is the only example tree โ€” each folder's 00_run_all_examples.dart both stands alone and is called by the master runner, so anything listed below is executed on every run.

SampleDemonstrates
example/samples/observable/ Observable scalars, composites, and observers
example/samples/beanlocator/ Registering and locating beans
example/samples/context/ Execution context & context entries
example/samples/scope_ids/ Hierarchical scope paths
example/samples/security/ Access controls, authorization, bearer auth
example/samples/crypto/ JWT / hashing helpers (via tom_crypto)
example/samples/runtime/ Environment / platform detection (via tom_basics)
example/samples/isolate_pooling/ Worker-pool execution
example/samples/logging/ Structured / isolate / remote logging
example/samples/resources/ Text & config resource providers
example/samples/preferences/ The preference codec's scalar, DateTime and reflected round-trips, and the /preferences contract
example/samples/scheduling/ The in-memory substrate, @TomScheduledJob discovery, every schedule kind against the missed-window policies, and what at-least-once asks of a job body
example/samples/json/ ยท example/samples/timezoned/ ยท example/samples/settings/ ยท example/samples/shutdown_cleanup/ ยท example/samples/http_connection/ ยท example/samples/little_things/ ยท example/samples/reflection/ The remaining utility modules

Run the whole set:

dart run build_runner build      # generate reflection (once / on signature change)
dart run --enable-asserts example/run_all_samples.dart

--enable-asserts is not optional. The samples check themselves with assert, and Dart compiles asserts away unless the flag is passed โ€” without it the examples would still print their output and still exit 0, while verifying nothing they claim. Rather than leave that trap open, every sample's main() opens with prepareSampleProcess() (example/sample_preamble.dart), which prints the correct command and exits 2 instead of running hollow.

That function is also where the process gets its platform identity. The framework builds its log header from TomPlatformUtils.current.getIsolateName(), and until something installs an implementation the fallback answers with the empty string โ€” so every framework log line reads <timestamp> - INFO โ€ฆ rather than <timestamp> main-main INFO โ€ฆ. Installing TomStandalonePlatformUtils in the shared preamble rather than in the master runner is what makes the prefix uniform across all three ways of starting a sample: the full run, a group runner, and a single file. No sample configures it, and none has to.

Every sample also runs on its own, which is usually what you want while reading one:

dart run --enable-asserts example/samples/observable/03_tom_class.dart

Samples that build reflected types call bootstrapSamples() (example/sample_bootstrap.dart) as the first statement of their main() instead of prepareSampleProcess() โ€” it does the preamble and then installs the generated reflection registry, which is the one place that happens. The rest reflect over nothing and skip it, because importing the registry costs roughly three seconds of compile time per entry point. That cost is also why the preamble lives in its own registry-free library: it lets the non-reflecting samples be prepared without paying for a registry they never touch.

The master runner cannot police the standalone path โ€” it drives samples through their group runners and ends in exit(0), so a sample that has no main(), never registers reflection, or never terminates is invisible to it. tool/run_standalone_examples.sh is the guard for that. It runs every sample by itself under a watchdog, failing on a hang as well as on a non-zero exit, and before the sweep it checks the commands this package hands out โ€” anywhere in it, not just in a sample header:

  • every documented dart run command pointing into example/ **names a file

that exists, which the sweep itself cannot check, since it finds samples with find rather than by reading the documentation; - every one of them passes --enable-asserts, so a reader who copies the command gets a run that verifies something; - every sample's main() calls prepareSampleProcess(), so a reader who types the command from memory and omits the flag is refused rather than shown a hollow green; - every sample contains at least one assert โ€” one that asserts nothing exits 0 whatever the API does, and has no verification for a missing flag to strip, so it slips past the check above as well; - every sample opens run() with print('=== NN: Title ===\n') carrying its own file number**, so that a reader watching a group runner's single continuous stream can tell which file each block came from. A sample that prints nothing contributes no block at all; one whose banner carries somebody else's number sends a reader to the wrong file, which is worse.

And after the sweep, one check that reads its output rather than the source: every sample appears in its own group's stream. The check above sees a banner anywhere in the file, which is not the same thing โ€” a banner in main() names the sample on the standalone path and leaves it anonymous inside the group runner, which calls run(). Reading the group runners' logs is what tells the two apart, and the sweep has just produced them.

./tool/run_standalone_examples.sh              # all of them
./tool/run_standalone_examples.sh observable   # one group

The group argument narrows the sweep only โ€” the five static checks always cover the whole package, because a wrong command is wrong for every reader, not only for the group under test. The stream check necessarily follows the sweep, so it covers whichever groups ran.

> Cross-package sample. Beyond these per-module examples, the shared > tom_core_samples/todo_domain > project models one todo-management domain on this kernel's reflection โ€” the > single entity set every Tom Core sample reuses. See the > samples map for the full learning path.

Usage

Namespacing with zone-propagated scopes

TomScope.withScope('checkout', () {
  TomScope.withScope('orderForm', () {
    print(TomScope.current.getScopePath()); // checkout.orderForm
  });
});

Resolving text resources

TomTextResourceProvider.setAppResourceProvider(
  TomTextResourceProvider.from({'greeting': 'Hello, {name}!'}),
);

Locating a bean

initializeBeanContext() discovers the classes annotated @TomComponent, selects one implementation per interface for the active environment and platform, and instantiates it. A consumer then holds a TomBean<T> handle, which resolves itself on first use:

@tomReflector
@TomComponent(UserService)
class UserServiceImpl implements UserService { /* โ€ฆ */ }

class OrderService {
  final TomBean<UserService> users = TomBean();

  Order create(int userId) => Order(users().getUser(userId));
}

Nothing is injected into a bean: it is built through its unnamed no-argument constructor, and each handle it declares resolves on its own. What discovery does provide is the choice โ€” which implementation a deployment gets โ€” and a start-up check that every declared handle can be resolved.

Ordered shutdown

TomShutdownCleanup registers TomDisposable / TomClosable resources and tears them down in a deterministic order on process exit.

Architecture

                tom_core_kernel
                      โ”‚
   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
observable  context  security  resources   runtime utils
 (TomObject) (scope) (TomAuth*) (providers) (logging, isolates,
                                             json, settings,
                                             shutdown, timezoned)
                      โ”‚
              tom_reflection  โ† introspection / serialization
              tom_basics ยท tom_crypto

The kernel is a single package with one barrel; modules live under lib/src/tombase/<module>/ and are mutually low-coupled โ€” context/scope and observable underpin the rest, security builds on both, and the runtime utilities are largely independent.

TypeResponsibility
TomObject<T>Observable value, the unit of reactive state
TomScopeZone-propagated hierarchical scope identifier
TomExecutionContextAmbient context carried through a call tree
TomAuthorization / TomAuthState Authorization decision + four-state model
TomAccessControlBase access-control rule
TomBean<T> / TomComponent Lazily resolved bean handle / the discovery annotation
TomWorker / TomWorkerPoolIsolate worker-pool execution
TomTextResourceProviderText resource resolution
TomShutdownCleanupOrdered teardown registry

Ecosystem

tom_basics ยท tom_crypto ยท tom_reflection
                 โ”‚
          tom_core_kernel        โ† you are here
        โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
tom_core_server  tom_core_flutter
                 โ”‚
          tom_flutter_ui

tom_core_kernel sits directly on the basics/crypto/reflection foundation and is the common dependency of the server and Flutter layers. See the repository map for the full picture.

Further documentation

  • Per-module guides under doc/ โ€” one folder per module

(doc/observable/, doc/security/, doc/scope_ids/, โ€ฆ). - doc/scheduling/scheduling.md โ€” the job scheduler: schedule kinds, retry and missed-window policies, the job store, and the lease lock that keeps one owner per job across processes. - Per-module API reference under doc/api_reference/ โ€” one summary per barrel-exported module plus a cross-module type index; start at api_summary_index.md. - tom_basics/doc/runtime/runtime.md โ€” the runtime guide (TomEnvironment, TomPlatform, TomRuntime). The code lives in tom_basics; the kernel barrel re-exports it. - tom_core_server โ€” the server runtime built on this kernel. - tom_core_flutter / tom_flutter_ui โ€” the client/UI layers.

Status

  • Version: 1.1.16
  • Tests: run with testkit :test or dart test. No count is given: a

count is a claim nothing checks, and every added test would be a README edit nobody makes. The version above is checked, because a wrong one is the claim that actively misleads. - License: BSD-3-Clause โ€” see LICENSE.

License
BSD 3-Clause License

Copyright (c) 2024-2026, Peter Nicolai Alexis Kyaw
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this
   list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright notice,
   this list of conditions and the following disclaimer in the documentation
   and/or other materials provided with the distribution.

3. Neither the name of the copyright holder nor the names of its
   contributors may be used to endorse or promote products derived from
   this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.