localHost()
Configure the zero-service local development host.
Little Harness: published on npm as little-harness@0.2.0-alpha.0 (2026-10-05). Sections labeled UNRELEASED describe source-only changes. Release status
localHost() configures the Local Host used by createHarness().
It is the default implementation target for the first fully working Little Harness SDK: local sessions, local artifacts, local traces, same-session queueing, and local Persistent Dir sources.
Import
import { localHost, localDir, projectDir } from "@little-stack/harness";Basic Usage
const harness = createHarness({
host: localHost({ dataDir: ".little-harness" }),
model,
system,
tools,
});dataDir defaults to .little-harness.
Storage Layout
Local Host stores sessions under dataDir with names that mirror harness paths:
session/ maps to /session, artifacts/ maps to /artifacts,
.agents/skills/ contains read-only materialized skills, and persistent/ contains the session checkout
for configured Persistent Dirs. workflows/ holds the event stores of workflow runs started by this
session's workflow tools (and dynamic-workflows/ those of one-shot plans): localHost() sets the
session's dataDir to this folder.
trace.ndjson is append-only newline-delimited JSON. Each line is a validated
lh.trace.v2 event. Large trace payloads are stored under
/artifacts/trace/... and referenced from events by contentRef.
API Signature
function localHost(options?: LocalHostOptions): HarnessHost;
type LocalHostOptions = {
dataDir?: string;
projectRoot?: string;
maxConcurrentSessions?: number;
sameSession?: "queue" | "reject";
sandbox?: HarnessSandboxMode | HarnessSandboxFactory;
orchestration?: HarnessOrchestrationServices;
};Parameters
dataDir
string
Local Host storage root. Defaults to .little-harness.
localHost({ dataDir: ".little-harness" })The host stores session folders, artifacts, traces, local persistent sources, and locks under this directory.
In development, .little-harness is intentionally inspectable. Do not put raw
provider credentials or app secrets inside mounted files unless your app's trace
redaction policy excludes those paths.
projectRoot
string
Root used to validate projectDir(...). Defaults to the current working
directory.
localHost({
dataDir: ".little-harness",
projectRoot: process.cwd(),
})projectDir("../docs") should fail unless it resolves inside projectRoot.
maxConcurrentSessions
number | undefined
Optional cap on simultaneously running sessions.
localHost({ maxConcurrentSessions: 4 })Same-session work is still serialized even when this allows multiple sessions to run concurrently.
This queue is process-local. It prevents two turns in the same Node.js runtime from mutating a session at once, but it is not a distributed lock across multiple serverless instances or separate processes.
sameSession
"queue" | "reject"
Controls what happens when a second turn targets a session that is already
running. Defaults to "queue".
localHost({ sameSession: "queue" })
localHost({ sameSession: "reject" })The invariant is fixed: one session never runs two turns at the same time.
sandbox
HarnessSandboxMode | HarnessSandboxFactory | undefined, where
HarnessSandboxMode is "auto" | "in-process" | "subprocess"
The sandbox the agent's bash tool runs in. Defaults to "auto".
UNRELEASED: published
0.2.0-alpha.0names this optionexecutionEnvironment(and the typesHarnessExecutionEnvironmentModeandHarnessExecutionEnvironmentFactory). See Alpha Migrations.
"auto"— in-process just-bash only when the turn'sruntime.pythonandruntime.javascriptare bothfalseand network is off;subprocessSandbox()otherwise. Because Python and JavaScript are on unless disabled, most turns get the subprocess sandbox."in-process"— in-process just-bash for every turn (the pre-0.2 default). A compatibility layer for trusted tool patterns, not an isolation boundary."subprocess"—subprocessSandbox()with its defaults for every turn.- a factory — for example
subprocessSandbox({ env, initTimeoutMs }),tieredSandbox({ nextTier }),justBashSandbox(), or your own.
localHost() // "auto"
localHost({ sandbox: "in-process" })
localHost({ sandbox: subprocessSandbox({ watchdogGraceMs: 2_000 }) })See Execution Environments for the shipped adapters and the custom-factory contract, and Execution Tiers for escalation.
orchestration
HarnessOrchestrationServices | undefined
Durable orchestration ledgers (tasks, continuations, wakeups, workflow queues,
results). Defaults to file-backed ledgers under dataDir.
import { localHost } from "@little-stack/harness";
import { createInMemoryOrchestrationServices } from "@little-stack/harness/host";
localHost({ orchestration: createInMemoryOrchestrationServices() });Custom backends implement the five-method DurableJsonStore and pass it to
createDurableOrchestrationServices({ rootDir, store }) — both shipped
backends share that same ledger core. HarnessOrchestrationServices,
DurableJsonStore and both factories come from @little-stack/harness/host.
Trace Inspection
Local Host traces are designed to be useful from both code and the command line.
From code, use onEvent on the harness or a single run:
const harness = createHarness({
host: localHost({ dataDir: ".little-harness" }),
model,
system,
tools,
onEvent: async (event) => {
await devTraceStore.append(event);
},
});From the terminal, the read-only CLI inspects the same Local Host data:
little-harness sessions --data-dir .little-harness
little-harness trace chat_123 --data-dir .little-harness --format pretty
little-harness files chat_123 --data-dir .little-harness
little-harness artifacts chat_123 --data-dir .little-harness
little-harness diff chat_123 --path /persistent/user/notes.md --data-dir .little-harness
little-harness doctor chat_123 --data-dir .little-harness
little-harness outcomes --data-dir .little-harnessThese inspection commands do not execute models or tools; the app still owns the model, tools, auth, policy, and runtime configuration.
localDir()
localDir() creates a local filesystem-backed Persistent Dir.
localDir({
harnessDir: "/persistent/user",
sourceDir: ({ extraBody }) =>
`persistent/users/${extraBody.userId}/memory`,
commit: "after-turn",
});harnessDir
string
Directory inside the harness filesystem, for example /persistent/user.
This is the path the agent sees.
sourceDir
string | ((context) => string | ProjectDirRef)
Backing directory outside the harness.
Plain relative strings resolve under localHost({ dataDir }).
sourceDir: "persistent/users/u_123/memory"A function's string result must be a relative path inside dataDir. A result that is
absolute, contains .., or resolves to dataDir itself or outside it throws a
HarnessPathError, so a request value such as a user id of ../company cannot
reach another folder. Validate ids before building the path.
Project files use projectDir(...).
sourceDir: projectDir("./docs")commit
"after-turn" | "manual" | "read-only"
Commit policy for this Persistent Dir.
commit: "read-only"
commit: "after-turn"
commit: "manual"read-only loads files into the harnessDir but rejects writes to that
directory immediately. after-turn buffers session writes and commits them
after the turn finishes. manual buffers session writes and skips the automatic
commit; call result.commitManual() or finished.commitManual() to commit that
completed turn later.
Little Harness does not infer write policy from names like company, project,
or user. Developers choose per directory.
projectDir()
projectDir() marks a local source as an intentional project-folder reference.
localDir({
harnessDir: "/persistent/project-docs",
sourceDir: projectDir("./docs"),
commit: "read-only",
});The resolved path must stay inside projectRoot.
Mixed Persistent Dirs
One harness can expose several Persistent Dirs with different sharing and write policies.
const harness = createHarness({
host: localHost(),
model,
persistentDirs: [
localDir({
harnessDir: "/persistent/company",
sourceDir: ({ extraBody }) =>
`persistent/companies/${extraBody.companyId}/knowledge`,
commit: "read-only",
}),
localDir({
harnessDir: "/persistent/user",
sourceDir: ({ extraBody }) =>
`persistent/users/${extraBody.userId}/memory`,
commit: "after-turn",
}),
],
});The agent sees both directories: /persistent/company/ and /persistent/user/.
The app decides whether a source is company-wide, project-wide, team-wide, or
user-specific through extraBody and the adapter's backing source.
Returns
localHost() returns a host object consumed by createHarness().
The exact object can stay opaque. The public contract is that it can create and resume local sessions, enforce same-session serialization, prepare the harness filesystem, record trace files, and commit local Persistent Dirs.