Documentation
¶
Overview ¶
Package nativeconfig applies ownership-aware MCP server patches to native JSON and JSONC client configuration files.
Index ¶
- Constants
- Variables
- func IsCommittedCleanup(err error) bool
- func ReadTransitionFileNoFollow(path string, limit int64) (body []byte, resultErr error)
- func ValidatePreparedTransition(paths Paths, p PreparedTransition) error
- func WriterLockPaths(paths Paths, codec Codec) ([]string, error)
- type Action
- type Codec
- type CommittedCleanupError
- type ExactFile
- type FileEffect
- type FileIO
- type FileSnapshot
- type Kernel
- func (kernel Kernel) Apply(req Request) (Receipt, error)
- func (kernel Kernel) ApplyBatch(requests []Request) (receipts []Receipt, err error)
- func (kernel Kernel) ApplyDialectTransition(req TransitionRequest) (receipts []Receipt, err error)
- func (kernel Kernel) BeginExactFile(path string) (*ExactFile, error)
- func (kernel Kernel) BeginPlainExactFile(path string) (*ExactFile, error)
- func (kernel Kernel) CheckDialectTransition(paths Paths, source, target Codec, owned []Receipt, names []string) (err error)
- func (kernel Kernel) CheckOpenCodeNamespace(paths Paths, proposed, previous []string) error
- func (kernel Kernel) CheckOpenCodeNamespaceForCodec(paths Paths, codec Codec, proposed, previous []string) error
- func (kernel Kernel) Inspect(paths Paths, codec Codec, name string, owned *Receipt) (present bool, exactlyOwned bool, err error)
- func (kernel Kernel) ReadExactFile(path string) (FileSnapshot, error)
- func (kernel Kernel) ReconcileDialectTransition(req TransitionRecoveryRequest) (observation TransitionObservation, resultErr error)
- func (kernel Kernel) RequireFileIO() error
- type OpenCodeNamespaceConflict
- type OpenCodeV2Options
- type OpenCodeV2Timeout
- type Paths
- type Placeholders
- type PreparedTransition
- type PreparedTransitionEntry
- type Receipt
- type Request
- type Server
- type TransitionEntry
- type TransitionObservation
- type TransitionRecoveryRequest
- type TransitionRequest
- type TransitionStateDecision
Constants ¶
const ( OpenCodeMCPObjectKind = "opencode_global_mcp_server" OpenCodeV2MCPObjectKind = "opencode_v2_global_mcp_server" )
const MaxTransitionConfigBytes = 2 << 20
Storage constraints, checked before a provider can persist authority and on every reopen. These do not claim to bound ordinary native config reads.
const MaxTransitionEntries = 256
Variables ¶
var ( ErrAmbiguousConfig = errors.New("both JSON and JSONC native config files exist") ErrCollision = errors.New("native MCP entry already exists") ErrNotOwned = errors.New("native MCP entry is not exactly owned") ErrMalformed = errors.New("native config is malformed") ErrConcurrentChange = errors.New("native config changed during patch") // ErrNativeMigrationRequired rejects unqualified mixed-generation roots. // A managed-leaf writer cannot convert unrelated V1 configuration to V2. ErrNativeMigrationRequired = errors.New("native_migration_required") )
var ErrOpenCodeV2Namespace = errors.New("OpenCode mcp.servers format is not yet supported by namespace preflight")
Functions ¶
func IsCommittedCleanup ¶
func ReadTransitionFileNoFollow ¶
ReadTransitionFileNoFollow reuses the native no-follow opener for private bounded journal/projection reads. Callers also enforce contained ancestors.
func ValidatePreparedTransition ¶
func ValidatePreparedTransition(paths Paths, p PreparedTransition) error
ValidatePreparedTransition checks stored bytes, mode, paths and every receipt against both strict native documents, rather than trusting a phase or hash alone.
func WriterLockPaths ¶
WriterLockPaths returns the exact lockfile identities used by the default native MCP writer, sorted by lock path in acquisition order. JSON is required; JSONC is optional. Both candidates are locked even when only one exists, so file selection and the ambiguous-config check happen under the locks. Paths must be absolute, clean and distinct, as for Apply; the codec must be supported.
This function performs no filesystem access, creates no locks and does not resolve symlinks or select a profile. Callers must validate their authority over the supplied paths. Do not append a suffix to the returned identities. Cline uses populated-directory .lock locks; all other codecs use regular-file .agentplugins.lock locks. This describes the default writer, not an injected NewWithLockAcquirer implementation, and does not acquire a lock on its behalf. Earlier binaries may acquire candidates in config-path order; multi-candidate interoperability requires that order to agree with the returned lock order.
Types ¶
type Codec ¶
type Codec string
const ( // CodecMCPServers is the explicit generic mcpServers profile: stdio uses // command/args/env/cwd and remote uses url/headers with no transport tag. // Clients with different native transport keys require their own codec. CodecMCPServers Codec = "mcpServers" CodecGemini Codec = "gemini-mcpServers" CodecOpenCode Codec = "opencode-mcp" CodecOpenCodeV2 Codec = "opencode-v2-mcp" CodecWindsurf Codec = "windsurf-mcpServers" CodecCline Codec = "cline-mcpServers" )
func OpenCodeCodecForDialect ¶
OpenCodeCodecForDialect is the closed prepared-profile/projection mapping. It accepts the serialized dialect so the config kernel does not depend on host qualification types. Stored ownership uses OpenCodeCodecForKind.
func OpenCodeCodecForKind ¶
OpenCodeCodecForKind is the closed stored-ownership decoder. Skills and package receipts are not MCP receipts. A claimed but unknown OpenCode kind fails closed rather than disappearing from verification or removal.
type CommittedCleanupError ¶
type CommittedCleanupError struct{ Err error }
CommittedCleanupError reports that the requested native config bytes and receipts were committed, but releasing the cooperating writer lock did not complete cleanly. Callers must keep the returned receipts and must not roll back other state that was committed in the same provider transaction. The wrapped error remains available so the cleanup degradation is observable.
func (*CommittedCleanupError) Error ¶
func (err *CommittedCleanupError) Error() string
func (*CommittedCleanupError) Unwrap ¶
func (err *CommittedCleanupError) Unwrap() error
type ExactFile ¶
type ExactFile struct {
// contains filtered or unexported fields
}
ExactFile holds the normal nativeconfig writer lock across a caller's native transaction. It deliberately leaves parsing and ownership digests to the caller, for clients whose native shape differs from the generic MCP codec. Call Close exactly once, after commit or rollback. The portable content-CAS limitation documented on conditionalFileIO also applies here.
func (*ExactFile) Apply ¶
Apply compares the original bytes/existence at the write boundary, and reads back even when WriteAtomic reports an error after making its write visible. On error the caller must invoke Rollback before releasing the lock.
func (*ExactFile) Close ¶
Close reports lock cleanup separately from the observed file effect. The caller decides whether its complete native transaction committed.
func (*ExactFile) Effect ¶
func (file *ExactFile) Effect() FileEffect
func (*ExactFile) Original ¶
func (file *ExactFile) Original() FileSnapshot
type FileEffect ¶
type FileEffect string
const ( FileUnchanged FileEffect = "unchanged" FileCommitted FileEffect = "committed" FileUncertain FileEffect = "uncertain" )
type FileIO ¶
type FileIO interface {
ReadNoFollow(path string) (body []byte, mode os.FileMode, exists bool, err error)
WriteAtomic(path string, body []byte, mode os.FileMode) error
RemoveNoFollow(path string) error
}
FileIO abstracts exact no-follow reads and atomic replacement. WriteAtomic preserves the requested file mode in the default implementation; platform metadata beyond the mode (for example ACLs and extended attributes) is not guaranteed to survive replacement.
type FileSnapshot ¶
FileSnapshot binds a mutation to exact bytes and existence. It is transient recovery data, never an ownership receipt or persisted client state.
type Kernel ¶
type Kernel struct {
// contains filtered or unexported fields
}
func NewWithFileIO ¶
func NewWithLockAcquirer ¶
func (Kernel) ApplyBatch ¶
ApplyBatch validates and renders related MCP entry mutations into one atomic replacement. The batch is all-or-none for cooperating agentplugins writers. See conditionalFileIO for the unavoidable portable race with clients that do not honor the same locks.
func (Kernel) ApplyDialectTransition ¶
func (kernel Kernel) ApplyDialectTransition(req TransitionRequest) (receipts []Receipt, err error)
ApplyDialectTransition uses the same candidate locks, document and verified conditional writer as ApplyBatch. The one replacement switches all selected leaves; rollback is conditional on our exact output. The noncooperating writer syscall race documented on conditionalFileIO remains unchanged. Errors without IsCommittedCleanup must not publish target ownership; unknown write/rollback status requires provider recovery from the prepared facts.
func (Kernel) BeginPlainExactFile ¶
BeginPlainExactFile retains the existing writer lock and ExactFile lifecycle. Only the default Darwin arm64 OS backend can authorize changed mutation. Unsupported metadata/platforms and custom IO still allow locked planning.
func (Kernel) CheckDialectTransition ¶
func (kernel Kernel) CheckDialectTransition(paths Paths, source, target Codec, owned []Receipt, names []string) (err error)
CheckDialectTransition is read-only admission for the closed same-ID provider transition. ApplyDialectTransition repeats every ownership/root proof under its commit lease with fully staged desired receipts.
func (Kernel) CheckOpenCodeNamespace ¶
CheckOpenCodeNamespace observes only the selected user-level config. It is process-inert and never modifies the config or starts OpenCode/MCP servers. previous names are omitted only when the same operation removes them.
func (Kernel) CheckOpenCodeNamespaceForCodec ¶
func (kernel Kernel) CheckOpenCodeNamespaceForCodec(paths Paths, codec Codec, proposed, previous []string) error
CheckOpenCodeNamespaceForCodec is the closed dialect-aware preflight for a future prepared host. The legacy helper stays V1. This is a conservative name collision policy, not proof of a running host's tool catalog or mixed roots. proposed contains only names that the prepared operation will leave active.
func (Kernel) Inspect ¶
func (kernel Kernel) Inspect(paths Paths, codec Codec, name string, owned *Receipt) (present bool, exactlyOwned bool, err error)
Inspect performs a strict read-only ownership check for one native entry.
func (Kernel) ReadExactFile ¶
func (kernel Kernel) ReadExactFile(path string) (FileSnapshot, error)
func (Kernel) ReconcileDialectTransition ¶
func (kernel Kernel) ReconcileDialectTransition(req TransitionRecoveryRequest) (observation TransitionObservation, resultErr error)
ReconcileDialectTransition holds exclusion across classification, authoritative state publication, and the immediate target->exact-preimage conditional write. Like Apply, portable CAS has a syscall-sized race against noncooperating hosts.
func (Kernel) RequireFileIO ¶
RequireFileIO reports that this kernel can Inspect and Apply. A zero Kernel has no FileIO and must not be used as a hide-default on skill-only native paths that never call ApplyBatch.
type OpenCodeNamespaceConflict ¶
OpenCodeNamespaceConflict is a conservative server-name collision: the servers could expose tools with the same callable ID. Tool catalogs are not queried, so this does not claim that a particular tool has collided.
func (*OpenCodeNamespaceConflict) Error ¶
func (e *OpenCodeNamespaceConflict) Error() string
type OpenCodeV2Options ¶
type OpenCodeV2Options struct {
Disabled bool `json:"disabled"`
Timeout *OpenCodeV2Timeout `json:"timeout,omitempty"`
}
OpenCodeV2Options is a closed subset of the pinned native MCP entry schema. Nil options render disabled:false and omit timeout.
type OpenCodeV2Timeout ¶
type OpenCodeV2Timeout struct {
Startup int `json:"startup,omitempty"`
Catalog int `json:"catalog,omitempty"`
Execution int `json:"execution,omitempty"`
}
OpenCodeV2Timeout uses milliseconds. Zero omits a field; configured values must be positive, as in @opencode/schema 2.0.21 Mcp.TimeoutConfig.
type Paths ¶
Paths names the mutually exclusive client config variants. If neither exists, JSON is created. The paths must be explicit and must not use HOME.
type Placeholders ¶
type PreparedTransition ¶
type PreparedTransition struct {
Path string
Original FileSnapshot
TargetBytes []byte
TargetHash string
SourceCodec, TargetCodec Codec
Entries []PreparedTransitionEntry
}
PreparedTransition contains bounded native facts only, never registry state. All mutable data is detached from both the request and the pending write. The recipient may retain/mutate this copy without changing committed bytes or returned receipts. TargetHash is SHA-256 of exact TargetBytes, distinct from the existing codec/name-domain ownership digest.
type PreparedTransitionEntry ¶
type Receipt ¶
type Receipt struct {
Version string `json:"version"`
Path string `json:"path"`
Codec Codec `json:"codec"`
Name string `json:"name"`
Digest string `json:"digest"`
}
Receipt identifies one exact projected entry. Ownership intentionally covers the complete entry: if another writer adds even one foreign field inside the entry, the receipt becomes stale and update/remove fail closed. It is safe to persist because it contains no original file bytes or unrelated config.
func DesiredReceipt ¶
func DesiredReceipt(resolvedPath string, codec Codec, name string, server Server, placeholders Placeholders) (Receipt, error)
DesiredReceipt projects one exact server entry without reading or mutating a config file. resolvedPath must be the already-selected clean absolute JSON or JSONC path. Apply returns the same receipt when it applies the same request.
type Request ¶
type Request struct {
Paths Paths
Codec Codec
Action Action
Name string
Server Server
Placeholders Placeholders
Owned *Receipt
// Desired optionally binds add/update to the exact projected receipt the
// caller staged. ApplyBatch compares it before any config write, including
// the selected JSON/JSONC path, so selection drift cannot commit first and
// fail only during a provider postcondition.
Desired *Receipt
}
type Server ¶
type Server struct {
// Trusted adapters set these only after portable expansion. They are not
// native fields or persisted payload flags; projection readers restore them.
StdioValuesResolved bool `json:"-"`
CWDResolved bool `json:"-"`
Type string `json:"type"`
Command string `json:"command,omitempty"`
Args []string `json:"args,omitempty"`
Env map[string]string `json:"env,omitempty"`
CWD string `json:"cwd,omitempty"`
URL string `json:"url,omitempty"`
Headers map[string]string `json:"headers,omitempty"`
// RemoteTransport selects codec-specific native keys where supported.
// OpenCode V2 accepts empty (native default) or streamable-http; V1 keeps
// rejecting an explicit transport. Unsupported transports are never dropped.
RemoteTransport string `json:"remote_transport,omitempty"`
// OpenCodeV2 carries only the pinned V2 disabled/timeout contract. Other
// codecs reject it rather than silently discard dialect-specific intent.
OpenCodeV2 *OpenCodeV2Options `json:"opencode_v2,omitempty"`
}
Server is the transport-aware neutral MCP shape accepted by the supported codecs. Type must be "stdio" or "remote". Portable placeholders are resolved once in raw args, env values, and cwd. RemoteTransport is codec-specific and is rejected unless the selected codec explicitly defines it.
type TransitionEntry ¶
type TransitionObservation ¶
type TransitionObservation string
const ( TransitionSource TransitionObservation = "source" TransitionTarget TransitionObservation = "target" )
type TransitionRecoveryRequest ¶
type TransitionRecoveryRequest struct {
Paths Paths
Prepared PreparedTransition
// Callbacks run under the same candidate lease as classification and restore.
// An error alone grants no restore authority. Desired or unknown never restores.
Decide func(TransitionObservation) (TransitionStateDecision, error)
Complete func(TransitionObservation) error
}
type TransitionRequest ¶
type TransitionRequest struct {
Paths Paths
SourceCodec, TargetCodec Codec
Entries []TransitionEntry
// PersistPrepared must durably persist the supplied facts before returning
// nil. It runs under the native file locks: callers acquire their operation
// lock first, and must not reenter the kernel here. The provider owns journal
// IDs, projection/skill linkage and recovery/state publication. A callback is
// required; kernel success alone does not complete that provider transaction.
PersistPrepared func(PreparedTransition) error
}
TransitionRequest is the private provider handoff for a managed-leaf OpenCode V1 <-> V2 transition, not host selection or registry authority. Entries must be strictly sorted by Name. No arbitrary document edits occur.
type TransitionStateDecision ¶
type TransitionStateDecision string
const ( TransitionStateOld TransitionStateDecision = "old" TransitionStateDesired TransitionStateDecision = "desired" TransitionStateUnknown TransitionStateDecision = "unknown" )