woods 2.0.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +108 -0
- data/CONTRIBUTING.md +135 -10
- data/README.md +1 -1
- data/docs/AGENT_GUIDE.md +19 -0
- data/docs/AGENT_SETUP.md +22 -2
- data/docs/BACKEND_MATRIX.md +7 -0
- data/docs/CLIENT_HOOKS.md +6 -0
- data/docs/CONFIGURATION_REFERENCE.md +133 -18
- data/docs/CONSOLE_MCP_SETUP.md +141 -12
- data/docs/EMBEDDING_MODELS.md +16 -19
- data/docs/EXTRACTOR_REFERENCE.md +219 -21
- data/docs/FAQ.md +11 -25
- data/docs/GETTING_STARTED.md +7 -1
- data/docs/INCREMENTAL_EXTRACTION.md +261 -19
- data/docs/INDEX_LAYOUT.md +5 -0
- data/docs/INTERNALS.md +9 -0
- data/docs/MCP_HTTP_TRANSPORT.md +59 -2
- data/docs/MCP_SERVERS.md +87 -8
- data/docs/MCP_TOOL_COOKBOOK.md +13 -55
- data/docs/NOTION_INTEGRATION.md +7 -1
- data/docs/PUBLISHED_INDEX.md +6 -0
- data/docs/README.md +6 -1
- data/docs/RETRIEVAL_GUIDE.md +17 -0
- data/docs/SOURCE_FRESHNESS.md +157 -5
- data/docs/TOKEN_BENCHMARK.md +10 -18
- data/docs/TROUBLESHOOTING.md +70 -14
- data/docs/UNBLOCKED_INTEGRATION.md +60 -8
- data/docs/UPGRADING_TO_2.md +184 -9
- data/docs/WATCH_DAEMON.md +97 -14
- data/exe/woods-console-mcp +2 -2
- data/exe/woods-mcp-http +16 -9
- data/lib/generators/woods/templates/woods.rb.tt +2 -1
- data/lib/tasks/woods.rake +23 -7
- data/lib/tasks/woods_checks.rake +2 -2
- data/lib/woods/agent_configuration/cli.rb +1 -1
- data/lib/woods/agent_configuration/layout.rb +16 -2
- data/lib/woods/agent_configuration/plan.rb +13 -3
- data/lib/woods/agent_configuration/planner_validation.rb +4 -2
- data/lib/woods/agent_configuration/preflight.rb +5 -3
- data/lib/woods/builder.rb +17 -57
- data/lib/woods/cache/cache_middleware.rb +68 -41
- data/lib/woods/chunking/contributor_chunks.rb +119 -0
- data/lib/woods/chunking/semantic_chunker.rb +44 -21
- data/lib/woods/console/adapter_family.rb +39 -0
- data/lib/woods/console/connection_manager.rb +56 -3
- data/lib/woods/console/credential_index.rb +33 -3
- data/lib/woods/console/embedded_executor.rb +431 -48
- data/lib/woods/console/model_validator.rb +8 -0
- data/lib/woods/console/rack_middleware.rb +68 -11
- data/lib/woods/console/redactor.rb +24 -10
- data/lib/woods/console/safe_context.rb +44 -7
- data/lib/woods/console/sql_noise_stripper.rb +41 -12
- data/lib/woods/console/sql_table_scanner.rb +45 -34
- data/lib/woods/console/sql_validator.rb +37 -2
- data/lib/woods/dependency_graph.rb +34 -10
- data/lib/woods/embedding/fake.rb +12 -0
- data/lib/woods/embedding/indexer.rb +195 -98
- data/lib/woods/embedding/input_budget.rb +67 -0
- data/lib/woods/embedding/openai.rb +70 -20
- data/lib/woods/embedding/provider.rb +37 -25
- data/lib/woods/embedding/text_preparer.rb +76 -32
- data/lib/woods/embedding/token_counter.rb +18 -81
- data/lib/woods/embedding/vector_configuration.rb +48 -0
- data/lib/woods/extraction_identities.rb +175 -0
- data/lib/woods/extractor.rb +304 -107
- data/lib/woods/extractors/action_cable_extractor.rb +8 -3
- data/lib/woods/extractors/assigned_value_discovery.rb +74 -0
- data/lib/woods/extractors/class_declarations.rb +121 -0
- data/lib/woods/extractors/configuration_extractor.rb +11 -3
- data/lib/woods/extractors/declaration_ancestry.rb +92 -0
- data/lib/woods/extractors/event_extractor.rb +8 -0
- data/lib/woods/extractors/graphql_extractor.rb +134 -77
- data/lib/woods/extractors/job_extractor.rb +5 -1
- data/lib/woods/extractors/lib_extractor.rb +132 -15
- data/lib/woods/extractors/mailer_extractor.rb +3 -5
- data/lib/woods/extractors/manager_extractor.rb +7 -21
- data/lib/woods/extractors/migration_declaration.rb +87 -0
- data/lib/woods/extractors/migration_extractor.rb +5 -39
- data/lib/woods/extractors/phlex_extractor.rb +6 -2
- data/lib/woods/extractors/policy_extractor.rb +9 -5
- data/lib/woods/extractors/poro_extractor.rb +112 -53
- data/lib/woods/extractors/pundit_extractor.rb +11 -6
- data/lib/woods/extractors/scheduled_job_extractor.rb +45 -4
- data/lib/woods/extractors/serializer_extractor.rb +34 -22
- data/lib/woods/extractors/shared_utility_methods.rb +18 -1
- data/lib/woods/extractors/source_nesting.rb +142 -106
- data/lib/woods/extractors/standalone_module_discovery.rb +123 -0
- data/lib/woods/extractors/state_machine_extractor.rb +46 -40
- data/lib/woods/extractors/view_component_extractor.rb +9 -7
- data/lib/woods/flow_assembler.rb +4 -1
- data/lib/woods/generation.rb +25 -0
- data/lib/woods/hooks/context_hint.rb +7 -2
- data/lib/woods/mcp/bearer_auth.rb +1 -1
- data/lib/woods/mcp/bootstrapper.rb +33 -7
- data/lib/woods/mcp/config_resolver.rb +26 -7
- data/lib/woods/mcp/index_reader.rb +125 -24
- data/lib/woods/mcp/index_reader_pinning.rb +16 -0
- data/lib/woods/mcp/origin_guard.rb +24 -77
- data/lib/woods/mcp/origin_policy.rb +124 -0
- data/lib/woods/mcp/renderers/markdown_renderer.rb +7 -1
- data/lib/woods/mcp/renderers/plain_renderer.rb +3 -1
- data/lib/woods/mcp/search_results.rb +7 -1
- data/lib/woods/mcp/server.rb +24 -4
- data/lib/woods/module_reconciliation.rb +151 -0
- data/lib/woods/path_dispatcher.rb +7 -2
- data/lib/woods/railtie_support.rb +8 -0
- data/lib/woods/rake_helpers.rb +43 -11
- data/lib/woods/release.rb +1 -1
- data/lib/woods/resilience/index_validator.rb +8 -3
- data/lib/woods/resilience/retryable_provider.rb +18 -1
- data/lib/woods/resolved_config.rb +68 -8
- data/lib/woods/retrieval/context_assembler.rb +3 -3
- data/lib/woods/retrieval/lexical_assembler.rb +3 -2
- data/lib/woods/retrieval/scope.rb +18 -2
- data/lib/woods/retrieval/source_evidence.rb +14 -2
- data/lib/woods/source_contributor_validation.rb +78 -0
- data/lib/woods/source_contributors.rb +116 -0
- data/lib/woods/source_inputs/handoff.rb +37 -0
- data/lib/woods/source_inputs/launcher.rb +53 -13
- data/lib/woods/source_inputs/manifest.rb +84 -3
- data/lib/woods/source_inputs/private_key.rb +44 -12
- data/lib/woods/source_inputs/scanner.rb +98 -27
- data/lib/woods/source_inputs/scopes.rb +1 -1
- data/lib/woods/source_inputs/session.rb +147 -15
- data/lib/woods/source_inputs/stable_reader.rb +127 -0
- data/lib/woods/source_inputs/status.rb +40 -8
- data/lib/woods/source_inputs/verifier.rb +28 -5
- data/lib/woods/source_path_encoding.rb +33 -0
- data/lib/woods/source_references/cache.rb +284 -0
- data/lib/woods/source_references/collector.rb +120 -0
- data/lib/woods/source_references/extraction.rb +185 -0
- data/lib/woods/source_references/inputs.rb +134 -0
- data/lib/woods/source_references/parser_adapter.rb +134 -0
- data/lib/woods/source_references/pass.rb +152 -0
- data/lib/woods/source_references/prism_adapter.rb +116 -0
- data/lib/woods/source_references/registry.rb +178 -0
- data/lib/woods/source_references/runtime_lookup.rb +127 -0
- data/lib/woods/source_references/value_class.rb +82 -0
- data/lib/woods/storage/metadata_store.rb +4 -1
- data/lib/woods/storage/qdrant.rb +2 -2
- data/lib/woods/unblocked/client.rb +12 -7
- data/lib/woods/unblocked/document_builder.rb +4 -1
- data/lib/woods/unblocked/exporter.rb +127 -37
- data/lib/woods/unblocked/sync_manifest.rb +137 -21
- data/lib/woods/unblocked/uri_migration.rb +105 -0
- data/lib/woods/util/host_guard.rb +3 -2
- data/lib/woods/version.rb +1 -1
- data/lib/woods/watch/catch_up.rb +138 -0
- data/lib/woods/watch/claim_lease.rb +150 -0
- data/lib/woods/watch/cli.rb +26 -2
- data/lib/woods/watch/daemon.rb +80 -59
- data/lib/woods/watch/installation/options.rb +1 -1
- data/lib/woods/watch/installation/receipt.rb +6 -1
- data/lib/woods/watch/managed_child.rb +1 -1
- data/lib/woods/watch/supervisor.rb +1 -1
- data/lib/woods/watch/tree_scan.rb +14 -2
- data/plugin/.claude-plugin/plugin.json +1 -1
- data/plugin/hooks/adapters/normalize.rb +3 -2
- data/plugin/hooks/woods-input-rules.sh +4 -0
- data/plugin/hooks/woods-refresh.sh +15 -7
- data/plugin/hooks/woods-session-start.sh +60 -3
- data/plugin/skills/woods-diagnose/SKILL.md +336 -2
- data/plugin/skills/woods-investigate/SKILL.md +11 -0
- data/plugin/skills/woods-mcp-config/SKILL.md +86 -0
- data/plugin/skills/woods-setup/SKILL.md +56 -1
- metadata +34 -5
|
@@ -115,7 +115,7 @@ Columns:
|
|
|
115
115
|
| `include_framework_sources` | Boolean | `true` | user-settable | Extract Rails and gem source code |
|
|
116
116
|
| `concurrent_extraction` | Boolean | `false` | user-settable | Enable parallel extraction (experimental) |
|
|
117
117
|
| `vector_store` / `metadata_store` / `graph_store` / `embedding_provider` | Symbol | n/a | preset-derived | Adapter types. Set by presets; override individually to mix stacks. |
|
|
118
|
-
| chars-per-token ratio (used by ContextAssembler, TextPreparer, Builder, cost_model) | Float | `4.0` (OpenAI) / `1.5` (Ollama) | computed |
|
|
118
|
+
| chars-per-token ratio (used by ContextAssembler, TextPreparer, Builder, cost_model) | Float | `4.0` (OpenAI) / `1.5` (Ollama) | computed | A sizing estimate derived from the active provider via `Woods::TokenUtils.chars_per_token_for(...)`, not a proof of token count. Complete embedding admission uses the provider-specific bound described below. |
|
|
119
119
|
|
|
120
120
|
## Embedding options
|
|
121
121
|
|
|
@@ -136,6 +136,26 @@ config.embedding_options = {
|
|
|
136
136
|
}
|
|
137
137
|
```
|
|
138
138
|
|
|
139
|
+
`dimensions:` explicitly requests an output width; use it to reduce a
|
|
140
|
+
`text-embedding-3-small` or `text-embedding-3-large` vector. The legacy singular
|
|
141
|
+
`dimension:` remains an alias for this explicit request; if both are supplied,
|
|
142
|
+
they must agree. `expected_dimensions:` records an expected output width
|
|
143
|
+
without requesting a reduction. Standalone snapshot restoration uses that
|
|
144
|
+
separate channel. Provider responses must match the expected width before
|
|
145
|
+
Woods accepts them.
|
|
146
|
+
|
|
147
|
+
`text-embedding-ada-002` has a fixed width of 1,536. A matching declared width
|
|
148
|
+
is accepted without sending the API's unsupported `dimensions` parameter;
|
|
149
|
+
other widths are refused before a request.
|
|
150
|
+
|
|
151
|
+
Embedding snapshots store observed `dimension` separately from
|
|
152
|
+
`requested_dimensions`. Standalone readers restore both. For legacy snapshots
|
|
153
|
+
without the latter field, Woods restores a reduction only when the model is
|
|
154
|
+
exactly `text-embedding-3-small` or `text-embedding-3-large` and the recorded
|
|
155
|
+
width is below that model's native width. It does not infer support for
|
|
156
|
+
reductions from arbitrary model names. Store and snapshot width checks still
|
|
157
|
+
apply; changing models or widths requires rebuilding a compatible vector store.
|
|
158
|
+
|
|
139
159
|
OpenAI embedding batches are sent in slices of at most 36 texts, preserving
|
|
140
160
|
input order. For inputs within the API's 8,192-token per-text limit, this stays
|
|
141
161
|
below both the 2,048-input limit and the 300,000-token total request limit.
|
|
@@ -160,14 +180,20 @@ config.embedding_options = {
|
|
|
160
180
|
|
|
161
181
|
The provider reads `model:`, `host:`, and `num_ctx:` from `embedding_options`. `num_ctx` is auto-selected from a per-model registry (`nomic-embed-text` → 2048, `bge-m3` → 8192, `mxbai-embed-large` → 512, `snowflake-arctic-embed` → 512, `snowflake-arctic-embed2` → 8192, `all-minilm` → 512). Unknown models fall back to 2048, matching Ollama's conservative embedding default. Set `num_ctx:` explicitly only when running a model with a known-larger native context that isn't in the registry yet.
|
|
162
182
|
|
|
183
|
+
An explicit `dimensions:` option (or its legacy `dimension:` alias) is sent to
|
|
184
|
+
Ollama and requires server/model support. A stored width is restored through
|
|
185
|
+
`expected_dimensions:` instead.
|
|
186
|
+
Discovering a width through a probe never adds `dimensions` to later requests.
|
|
187
|
+
|
|
163
188
|
**Why `num_ctx` is capped at the native context.** Ollama has an open regression ([ollama/ollama#14186](https://github.com/ollama/ollama/issues/14186)) where `options.num_ctx` does not lift the effective ceiling on `/api/embed` for models whose native context is smaller than the override. Woods advertises the native ceiling so the chunker sizes inputs to what Ollama will actually accept.
|
|
164
189
|
|
|
165
|
-
**
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
190
|
+
**Input counting (included in Woods 2.1).** Complete inputs include metadata
|
|
191
|
+
prefixes. Known OpenAI embedding models use a conservative byte upper bound;
|
|
192
|
+
Ollama/custom model counts remain estimates. Woods requests `truncate: false`
|
|
193
|
+
from Ollama and refuses/splits oversized inputs without dropping source.
|
|
194
|
+
Installing `tokenizers` no longer implicitly downloads BERT or establishes an
|
|
195
|
+
exact count for an unrelated model. A caller-supplied local tokenizer must match
|
|
196
|
+
the selected model. See the model guide for limitations.
|
|
171
197
|
|
|
172
198
|
See [EMBEDDING_MODELS.md](EMBEDDING_MODELS.md) for the full model comparison and the procedure for adding a new model to the registry.
|
|
173
199
|
|
|
@@ -188,6 +214,39 @@ Anything responding to `#embed` and `#embed_batch` can be assigned directly, it
|
|
|
188
214
|
config.embedding_provider = MyCompany::CustomEmbedder.new(endpoint: internal_url)
|
|
189
215
|
```
|
|
190
216
|
|
|
217
|
+
Included in Woods 2.1: snapshots capture the effective settings of injected
|
|
218
|
+
Ollama, OpenAI, and Fake instances, including through Woods' retry and embedding
|
|
219
|
+
cache wrappers. An injected instance takes precedence over unused
|
|
220
|
+
`embedding_options`. Ollama retains its model, host, context size, read timeout,
|
|
221
|
+
and vector settings; OpenAI and Fake retain their model and vector settings.
|
|
222
|
+
Capturing constructor settings makes no requests. When a live `provider:` is
|
|
223
|
+
passed to `ResolvedConfig.from_configuration`, its existing dimension-discovery
|
|
224
|
+
behavior remains available. The explicit request width remains separate from
|
|
225
|
+
the observed width.
|
|
226
|
+
|
|
227
|
+
API keys are never stored; standalone OpenAI restoration still requires
|
|
228
|
+
`OPENAI_API_KEY`. Only plain HTTP(S) origins can be persisted as Ollama hosts
|
|
229
|
+
(an optional trailing `/` is allowed). URLs containing user information, a path
|
|
230
|
+
prefix, query, or fragment are omitted and marked `requires_host_provider`.
|
|
231
|
+
Those components may be legitimate routing settings, but can also carry
|
|
232
|
+
credentials; Woods refuses to reconstruct a different endpoint by dropping them.
|
|
233
|
+
The same restriction applies when reading older snapshot endpoints.
|
|
234
|
+
|
|
235
|
+
**Reader compatibility:** snapshots marked `requires_host_provider` need a
|
|
236
|
+
supporting reader. Older schema-1 readers cannot enforce that marker and may
|
|
237
|
+
silently select defaults; do not use them for implicit restoration of these
|
|
238
|
+
snapshots. Ordinary built-in snapshots with non-secret settings remain
|
|
239
|
+
schema-1 compatible.
|
|
240
|
+
|
|
241
|
+
Custom providers and custom wrappers are also marked `requires_host_provider`;
|
|
242
|
+
Woods does not serialize arbitrary client state or infer a built-in from a custom
|
|
243
|
+
class name. Embedding in the configured host continues to work. To serve such a
|
|
244
|
+
snapshot semantically, provide an explicitly configured compatible provider in
|
|
245
|
+
the reader's host initializer. Implicit standalone restoration reports a
|
|
246
|
+
configuration error instead of choosing defaults. Explicit lexical retrieval
|
|
247
|
+
does not restore or call an embedding provider. Record the loaded revision
|
|
248
|
+
before relying on these snapshot improvements.
|
|
249
|
+
|
|
191
250
|
## Storage options
|
|
192
251
|
|
|
193
252
|
| Option | Type | Default | Description |
|
|
@@ -264,6 +323,15 @@ worktree when overriding it. Existing databases at the old configured output
|
|
|
264
323
|
path are not moved or deleted; run `woods:embed` for the selected index after
|
|
265
324
|
upgrading to populate its default metadata database.
|
|
266
325
|
|
|
326
|
+
**Included in Woods 2.1:** embedding with dump-backed vectors reconciles the
|
|
327
|
+
complete SQLite identity inventory, including records without vectors and
|
|
328
|
+
same-name units with distinct types. A full rebuild removes vanished records,
|
|
329
|
+
including when the new corpus is empty. Incremental runs retain the existing
|
|
330
|
+
empty-input and 30% purge guards. Both modes still refuse incomplete extraction
|
|
331
|
+
input before changing stores. Standalone MCP opens the index's SQLite metadata
|
|
332
|
+
before restoring type filters on hydrated vectors, using that same metadata
|
|
333
|
+
store for retrieval. No database migration or embedding format change is required.
|
|
334
|
+
|
|
267
335
|
Requires the `sqlite3` gem in your host bundle. Rails apps backed by
|
|
268
336
|
MySQL or PostgreSQL won't have it by default, selecting `:sqlite`
|
|
269
337
|
without it raises `Woods::ConfigurationError` with install
|
|
@@ -326,9 +394,33 @@ overrides the wrapper defaults for `:embeddings` (24 hours) and `:context`
|
|
|
326
394
|
(15 minutes). `:memory` accepts `max_entries` (default 500); it ignores
|
|
327
395
|
`default_ttl` because each wrapper write supplies its domain TTL.
|
|
328
396
|
|
|
397
|
+
**Included in Woods 2.1:** retrieval contexts are scoped to one retriever
|
|
398
|
+
instance, including when Redis or Solid Cache is shared by applications or
|
|
399
|
+
worktrees. Restarting the retriever starts a fresh context namespace. Reload
|
|
400
|
+
retires only that instance's namespace, including results still in flight;
|
|
401
|
+
already running requests may finish against the previous corpus. Retired entries
|
|
402
|
+
expire according to their configured TTL or backend eviction; disabling both
|
|
403
|
+
can retain unused entries indefinitely. Embedding-vector caches are separate and survive context invalidation.
|
|
404
|
+
|
|
405
|
+
Embedding entries are scoped to the provider family, endpoint, model, requested
|
|
406
|
+
and declared widths, and relevant input options. Cache lookups never call a
|
|
407
|
+
provider's probing `dimensions` method. Keys contain hashes rather than raw
|
|
408
|
+
endpoint credentials or API keys. Malformed cached vectors and known-width
|
|
409
|
+
mismatches are refused. This configuration key format starts a fresh embedding
|
|
410
|
+
cache after upgrading; old entries expire under their existing TTLs.
|
|
411
|
+
|
|
412
|
+
Custom provider objects may expose a pure, JSON-compatible `cache_identity`
|
|
413
|
+
containing every setting that affects their vectors, plus a non-probing
|
|
414
|
+
`configured_dimensions` reader for width validation. Otherwise each cache
|
|
415
|
+
wrapper has its own namespace; cached vectors are still checked for valid
|
|
416
|
+
numeric content and consistent batch widths. Configuration changes require a
|
|
417
|
+
new provider and wrapper. A remotely replaced model under an unchanged name
|
|
418
|
+
still requires an explicit cache/model-version change and re-embedding.
|
|
419
|
+
|
|
329
420
|
`Woods::Cache.cache_key` length-prefixes every component, including a single
|
|
330
|
-
component, so different argument counts cannot share a response.
|
|
331
|
-
|
|
421
|
+
component, so different argument counts cannot share a response. This component
|
|
422
|
+
encoding is unchanged and independent of the retriever's context namespace;
|
|
423
|
+
the embedding wrapper uses the new identity above. Custom callers
|
|
332
424
|
using single-component keys must clear their affected persistent cache domain
|
|
333
425
|
when upgrading, since older unprefixed entries can alias the new encoding;
|
|
334
426
|
subsequent calls refill it normally. Namespace clearing still covers both formats.
|
|
@@ -496,22 +588,35 @@ there is no full population count to publish. Both caps must be a positive Integ
|
|
|
496
588
|
|
|
497
589
|
| Option | Type | Default | Description |
|
|
498
590
|
|--------|------|---------|-------------|
|
|
499
|
-
| `session_tracer_enabled` | Boolean | `false` | Enable session tracing middleware |
|
|
591
|
+
| `session_tracer_enabled` | Boolean | `false` | Enable session tracing middleware; set with its store in `config/application.rb` before Railtie initialization |
|
|
500
592
|
| `session_tracer_allow_production` | Boolean | `false` | Explicitly allow session tracing in `Rails.env.production?`. Without this opt-in, the Railtie warns and leaves the tracer disabled even when `session_tracer_enabled` is true. Review trace contents, retention, and access controls before enabling it. |
|
|
501
593
|
| `session_store` | Object | `nil` | Store backend: `FileStore`, `RedisStore`, or `SolidCacheStore` |
|
|
502
594
|
| `session_id_proc` | Proc | `nil` | Custom proc to extract session ID from requests |
|
|
503
595
|
| `session_exclude_paths` | Array<String> | `[]` | Path patterns to exclude from tracing |
|
|
504
596
|
|
|
597
|
+
Configure the complete tracer setup in `config/application.rb`, after Woods is
|
|
598
|
+
required and before Rails initialization. A `config/initializers/woods.rb`
|
|
599
|
+
assignment is too late to mount this middleware; Woods warns and tracing stays
|
|
600
|
+
disabled. Restart the server after changing it.
|
|
601
|
+
|
|
505
602
|
```ruby
|
|
506
|
-
|
|
603
|
+
# config/application.rb, after Bundler.require(*Rails.groups)
|
|
604
|
+
require 'woods/session_tracer/file_store'
|
|
507
605
|
|
|
508
|
-
|
|
509
|
-
config.
|
|
510
|
-
|
|
511
|
-
)
|
|
512
|
-
|
|
606
|
+
Woods.configure do |config|
|
|
607
|
+
config.session_tracer_enabled = true
|
|
608
|
+
config.session_store = Woods::SessionTracer::FileStore.new(
|
|
609
|
+
base_dir: File.expand_path('../tmp/session_traces', __dir__)
|
|
610
|
+
)
|
|
611
|
+
config.session_exclude_paths = ['/health', '/metrics', '/assets']
|
|
612
|
+
end
|
|
513
613
|
```
|
|
514
614
|
|
|
615
|
+
The Rails middleware records traces. Reading them with `session_trace` additionally
|
|
616
|
+
requires a custom Index Server process configured with the same store; the
|
|
617
|
+
packaged Index executable does not load application initializers. See
|
|
618
|
+
[conditional capabilities](MCP_SERVERS.md#conditional-index-capabilities).
|
|
619
|
+
|
|
515
620
|
### File session retention
|
|
516
621
|
|
|
517
622
|
`FileStore` accepts `ttl:` in seconds (default `nil`, expiration disabled),
|
|
@@ -682,8 +787,8 @@ deployment guide including defense layers.
|
|
|
682
787
|
| `console_mcp_enabled` | Boolean | `false` | Master switch. When `false`, stdio exits and the mounted Console middleware passes requests through to Rails. |
|
|
683
788
|
| `console_mcp_http_enabled` | Boolean | `true` | HTTP transport switch; effective only while the master switch is on. Set `false` for stdio-only use without HTTP token validation or an active HTTP endpoint. Read at request time. |
|
|
684
789
|
| `console_mcp_token` | String | `ENV['WOODS_CONSOLE_MCP_TOKEN']` or `nil` | Bearer token required on every enabled Console HTTP request. With both Console flags enabled, production boot raises on a missing token; other environments warn and requests fail closed with 401. A configured token shorter than 32 characters raises at boot while HTTP is enabled. Explicit stdio-only configurations skip HTTP token validation. Generate with `SecureRandom.hex(32)`. |
|
|
685
|
-
| `console_mcp_allowed_origins` | Array\<String\> | `%w[http://localhost http://127.0.0.1 http://[::1]]` |
|
|
686
|
-
| `console_mcp_path` | String | `/mcp/console` | URL path
|
|
790
|
+
| `console_mcp_allowed_origins` | Array\<String\> | `%w[http://localhost http://127.0.0.1 http://[::1]]` | Shared guard/SDK allowlist (included in Woods 2.1). Exact cross-origin entries include the port; portless entries also permit same-authority traffic. Include the actual browser origin and non-loopback endpoint authority. Restart after changes. |
|
|
791
|
+
| `console_mcp_path` | String | `/mcp/console` | URL path captured when Rack middleware mounts. Set a custom path in `config/application.rb` before Railtie initialization, then restart. |
|
|
687
792
|
| `console_embedded_read_tools` | Boolean | `false` | Register `console_sql` and `console_query` in supported stdio and Rack modes. |
|
|
688
793
|
| `console_blocked_tables` | Array\<String\> | `Woods::DEFAULT_CONSOLE_BLOCKED_TABLES` | TableGate denylist (case-insensitive). Bare names match every schema; qualified names (`schema.table`) match exactly. |
|
|
689
794
|
| `console_redacted_columns` | Array\<String\> | `Woods::DEFAULT_CONSOLE_REDACTED_COLUMNS` | Column names whose values are replaced with `[REDACTED]` in responses, and which are refused as aggregate, scope, find, and order inputs. |
|
|
@@ -785,6 +890,11 @@ in its finalized development environment. See [startup and installation](WATCH_D
|
|
|
785
890
|
for the generator's explicit modes, portable receipt, update/removal, and the
|
|
786
891
|
separate supervision status. Raw task settings above remain compatible.
|
|
787
892
|
|
|
893
|
+
**Included in Woods 2.1 (#591):** blank/whitespace-only idle timeouts count as
|
|
894
|
+
unset consistently. `woods-watch --recover-claim INDEX --claim-token TOKEN`
|
|
895
|
+
provides explicit recovery only for a matching abandoned managed lifetime lease;
|
|
896
|
+
see [ownership recovery](WATCH_DAEMON.md#recovering-an-abandoned-managed-claim).
|
|
897
|
+
|
|
788
898
|
### Opt-in plugin refresh hooks
|
|
789
899
|
|
|
790
900
|
These settings control the plugin shell worker. Check installed
|
|
@@ -813,6 +923,11 @@ tasks for manual refreshes; hook transport is not a general shell execution API.
|
|
|
813
923
|
|
|
814
924
|
### Extraction rake tasks
|
|
815
925
|
|
|
926
|
+
The separate `woods-extract` launcher captures before Rails configuration runs:
|
|
927
|
+
custom `output_dir` applications must supply matching `--output` or `WOODS_OUTPUT`.
|
|
928
|
+
Its implicit default is `tmp/woods`; the Woods 2.1 #591 guard refuses a finalized
|
|
929
|
+
configuration mismatch before publication. See [launcher output selection](SOURCE_FRESHNESS.md#establish-a-fresh-baseline).
|
|
930
|
+
|
|
816
931
|
| Variable | Default | Purpose |
|
|
817
932
|
|----------|---------|---------|
|
|
818
933
|
| `WOODS_OUTPUT` | `Woods.configuration.output_dir` | Overrides the output directory for extraction/watch and embedding tasks without editing the initializer; embedding also places its default SQLite metadata database there. Explicit database options take precedence. |
|
data/docs/CONSOLE_MCP_SETUP.md
CHANGED
|
@@ -209,7 +209,18 @@ Rails/MCP host. If a browser-based client sends an `Origin` header from a
|
|
|
209
209
|
different host, include that exact origin too. This allow-list controls both
|
|
210
210
|
DNS-rebinding Host checks and browser CORS; keep Rails' own `config.hosts`, TLS,
|
|
211
211
|
and proxy rules aligned with it. Server-to-server clients normally omit
|
|
212
|
-
`Origin`, but
|
|
212
|
+
`Origin`, but a present request `Host` must still be allowed. Supporting security
|
|
213
|
+
revisions deliberately allow configured non-loopback Hosts through the SDK too;
|
|
214
|
+
2.0.0 could refuse them at that inner layer despite the Woods allowlist. This
|
|
215
|
+
widening is limited to the configured authorities and retains bearer auth. The
|
|
216
|
+
same immutable policy makes preflight acceptance agree with SDK dispatch. List exact browser ports for
|
|
217
|
+
cross-port traffic, including loopback; a portless origin additionally permits
|
|
218
|
+
same-authority traffic, not arbitrary cross-port access. Console defaults remain
|
|
219
|
+
HTTP loopback, while the Index Server defaults include HTTP and HTTPS loopback.
|
|
220
|
+
Default HTTP(S) ports are equivalent to their omitted form. An explicit list
|
|
221
|
+
replaces default browser origins, including loopback origins; add loopback entries
|
|
222
|
+
if those clients are needed. Invalid entries fail at boot naming the entry.
|
|
223
|
+
Restart after changing the list. See [HTTP origin matching](MCP_HTTP_TRANSPORT.md#browser-origins-dns-rebinding-defense).
|
|
213
224
|
|
|
214
225
|
Do not mount `Woods::Console::RackMiddleware` by itself. The Railtie composes
|
|
215
226
|
`OriginGuard`, `BearerAuth`, and the Console middleware in the supported order.
|
|
@@ -329,6 +340,21 @@ user: deploy
|
|
|
329
340
|
command: cd /app && bundle exec rake woods:console
|
|
330
341
|
```
|
|
331
342
|
|
|
343
|
+
**Included in Woods 2.1:** direct mode prefers an existing executable `bin/rake`
|
|
344
|
+
in the application's selected directory, falling back to `bundle exec rake`.
|
|
345
|
+
Relative `directory` values resolve from the launcher's initial working directory.
|
|
346
|
+
Explicit `command` settings win. Docker and SSH modes retain their remote default;
|
|
347
|
+
set `command` explicitly when that application uses another task entry point.
|
|
348
|
+
|
|
349
|
+
**Included in Woods 2.1:** the launcher rejects unknown keys, nested
|
|
350
|
+
`connection:`/`console:` sections, blank or non-string values, and options that
|
|
351
|
+
do not apply to the selected mode. Keep `mode` and `command` at the top level;
|
|
352
|
+
use `directory` for direct mode, `container` for Docker, and `host`/`user` for SSH.
|
|
353
|
+
Configure access controls and redaction in the Rails initializer, not this
|
|
354
|
+
process-launch file. Move legacy nested launch settings to the matching example
|
|
355
|
+
above. An empty file or mapping still selects the default direct command;
|
|
356
|
+
supported `command`/`directory` overrides can omit `mode` for direct execution.
|
|
357
|
+
|
|
332
358
|
Override config path with environment variable:
|
|
333
359
|
|
|
334
360
|
```bash
|
|
@@ -376,6 +402,13 @@ servers register only executable tools: the 9 Tier 1 tools by default, plus
|
|
|
376
402
|
| `console_association_count` | Count associated records for a specific record |
|
|
377
403
|
| `console_recent` | Recently created/updated records (max 50) |
|
|
378
404
|
|
|
405
|
+
**Included in Woods 2.1:** `console_association_count` returns `0` or `1` for
|
|
406
|
+
`belongs_to` and `has_one`, including missing targets. It counts the association's
|
|
407
|
+
relation, applying `scope` to the target model; collection associations retain
|
|
408
|
+
their ordinary count. Target-table and scope-column checks still apply before
|
|
409
|
+
association reads. The tool does not call a custom association getter to count
|
|
410
|
+
an already loaded model object.
|
|
411
|
+
|
|
379
412
|
### Tier 2: Domain-aware (9 tools): inventory only, not executable
|
|
380
413
|
|
|
381
414
|
| Tool | Description |
|
|
@@ -420,9 +453,21 @@ servers register only executable tools: the 9 Tier 1 tools by default, plus
|
|
|
420
453
|
|
|
421
454
|
## Configuration options
|
|
422
455
|
|
|
423
|
-
Set
|
|
456
|
+
Set the request-time flags and protection settings below in your Rails initializer.
|
|
457
|
+
A **custom endpoint path** must be set earlier, after Woods is required in
|
|
458
|
+
`config/application.rb`, because the Railtie captures it while mounting middleware:
|
|
424
459
|
|
|
425
460
|
```ruby
|
|
461
|
+
# config/application.rb, after Bundler.require(*Rails.groups)
|
|
462
|
+
Woods.configure { |config| config.console_mcp_path = '/internal/woods-console' }
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
Restart after changing the path. Leaving the default `/mcp/console` requires no
|
|
466
|
+
path assignment. Session tracing also needs early configuration; follow
|
|
467
|
+
[session tracer options](CONFIGURATION_REFERENCE.md#session-tracer-options).
|
|
468
|
+
|
|
469
|
+
```ruby
|
|
470
|
+
# config/initializers/woods.rb
|
|
426
471
|
Woods.configure do |config|
|
|
427
472
|
# Master on/off switch for the Console MCP feature (Layer 0). Default: false.
|
|
428
473
|
# Applies to every transport: stdio, launcher wrapper, and Rack.
|
|
@@ -432,9 +477,6 @@ Woods.configure do |config|
|
|
|
432
477
|
# after configuring the layers below that match your threat model.
|
|
433
478
|
config.console_mcp_enabled = true
|
|
434
479
|
|
|
435
|
-
# URL path for the Rack middleware endpoint. Default: '/mcp/console'.
|
|
436
|
-
config.console_mcp_path = '/mcp/console'
|
|
437
|
-
|
|
438
480
|
# HTTP Origin + Host allow-list. Defaults to loopback only. Non-loopback
|
|
439
481
|
# Rack deployments must include their public MCP host; browser clients from
|
|
440
482
|
# another origin need that exact origin listed too.
|
|
@@ -596,6 +638,10 @@ Direct, unaliased selection of a redacted column stays allowed: the output heade
|
|
|
596
638
|
|
|
597
639
|
`console_sql` applies a stricter form of the same rule because arbitrary SQL can rename output headers. A protected identifier is accepted only as a direct, unaliased outer `SELECT` column; aliases, aggregates, predicates, CTE shapes, ordering/grouping uses, and an EAV value without its paired key are refused before adapter execution. Use `console_query` when a protected column must participate in a more complex structured query.
|
|
598
640
|
|
|
641
|
+
The security correction refuses PostgreSQL Unicode-escaped identifiers in
|
|
642
|
+
`console_sql` before execution. Use ordinary identifiers or standard quoted
|
|
643
|
+
identifiers instead. This restriction does not change the structured query tools.
|
|
644
|
+
|
|
599
645
|
`console_query`'s `having` is covered by the same oracle rule: an aggregate over a protected column (`MAX(amount) > ?`), a bare predicate on a redacted column (`salary > ?`), or a predicate on an EAV value column is refused, since repeated guesses reveal the protected value from whether a row is returned. EAV key predicates stay allowed so callers can select the rows whose paired values need redaction.
|
|
600
646
|
|
|
601
647
|
**Ships with a curated credential default list** (`Woods::DEFAULT_CONSOLE_REDACTED_COLUMNS`, 31 columns) covering Devise, Doorkeeper, Rodauth, has_secure_password, devise-two-factor, and common hand-rolled auth shapes: `password`, `password_digest`, `password_salt`, `encrypted_password`, `crypted_password`, `salt`, `otp_secret`, `encrypted_otp_secret`, `two_factor_secret`, `backup_codes`, `consumed_timestep`, `reset_password_token`, `confirmation_token`, `unlock_token`, `remember_token`, `invitation_token`, `access_token`, `refresh_token`, `auth_token`, `api_token`, `api_key`, `bearer_token`, `client_secret`, `webhook_secret`, `signing_secret`, `session_secret`, `private_key`, `encrypted_private_key`, `key_hash`, `token`, `secret`.
|
|
@@ -636,6 +682,9 @@ Redaction is defense-in-depth, prefer not storing plaintext secrets in database
|
|
|
636
682
|
|
|
637
683
|
### `console_redacted_key_values`
|
|
638
684
|
|
|
685
|
+
See [read policy compatibility](#read-policy-compatibility) for SQL column-list
|
|
686
|
+
restrictions, conservative typed masking, exact key spelling and binary columns.
|
|
687
|
+
|
|
639
688
|
Column-name redaction falls short when credentials are stored in a **key-value (EAV)** table, e.g. a Stripe Connect `authorizations` row of `{key: "stripe_access_token", value: "sk_live_..."}`. The column holding the secret is called `value`, which is generic: adding `value` to `console_redacted_columns` would over-redact every unrelated row in the table.
|
|
640
689
|
|
|
641
690
|
`console_redacted_key_values` takes one or more patterns that describe "when a row has `key_column` set to one of these names, redact its `value_column`":
|
|
@@ -700,7 +749,7 @@ these controls, in order:
|
|
|
700
749
|
|
|
701
750
|
1. `SqlValidator` rejects DML/DDL (`INSERT`/`UPDATE`/`DELETE`/`MERGE`/`DROP`/`TRUNCATE`/`ALTER`/`CREATE`/`REPLACE`), row-lock clauses (`FOR UPDATE`, `FOR SHARE`, `LOCK IN SHARE MODE`), writable CTEs (every `AS (...)` body, not just the first), `UNION`/`INTO`/`COPY`, multi-statement and comment-hidden injections, and most administrative keywords (`DO`, `SET`, `LISTEN`, `NOTIFY`, `CALL`, `LOAD`, `VACUUM`, `PREPARE`, transaction control, `EXPLAIN ANALYZE`) at the string level. Enforces a read-only **function allowlist** (`ALLOWED_FUNCTIONS`), anything not on it is rejected by name, quoted forms (`"pg_terminate_backend"(…)`) included. Only `SELECT`, `WITH…SELECT`, and plain `EXPLAIN` pass.
|
|
702
751
|
2. `TableGate` refuses any SQL, model, or join that touches a `console_blocked_tables` entry.
|
|
703
|
-
3. `SafeContext` wraps every request in a rolled-back transaction with
|
|
752
|
+
3. `SafeContext` wraps every request in a rolled-back transaction with an adapter-dependent statement timeout. **It does NOT cover async side effects**: ActiveJob `perform_later`, ActionMailer `deliver_later`, direct HTTP egress, `Thread.new`-spawned work, `after_rollback` callbacks, and writes through a different shard all execute as live. Treat the Console MCP as an admin-trust boundary, not a sandbox.
|
|
704
753
|
4. `CredentialScanner` + column/EAV redaction scrub results.
|
|
705
754
|
|
|
706
755
|
Keep the flag off when the host requires a narrower database capability.
|
|
@@ -718,7 +767,7 @@ supported transport (stdio, Docker/SSH launcher, and HTTP).
|
|
|
718
767
|
| 1 | Blocked tables | `console_blocked_tables` | Tool dispatch, before executor | Reject any tool call that touches a named table (model, table, or sql arg) |
|
|
719
768
|
| 2 | Credential scanner | `console_disabled_scanner_patterns` (`[:all]` to disable entirely) | After executor, before render | Content-shape redaction of credential-shaped strings anywhere in the response tree |
|
|
720
769
|
| 3 | Column + EAV redaction | `console_redacted_columns`, `console_redacted_key_values` | After executor, before Layer 2 | Identity-based redaction by column name and by key/value row shape |
|
|
721
|
-
| 4 | SqlValidator + SafeContext | built-in | Inside executor | SQL
|
|
770
|
+
| 4 | SqlValidator + SafeContext | built-in | Inside executor | SQL validation and function allowlist for `console_sql`; transaction rollback for every request |
|
|
722
771
|
|
|
723
772
|
Layers 0–3 are configured via `Woods.configure`. Layer 4 is always on and has no knobs. Observability hooks, `console.table_gate.rejected` for Layer 1, `console.credential_scan.hits` for Layer 2, emit structured log lines via `Woods::Observability::StructuredLogger` so operators can audit enforcement without scraping MCP wire traffic.
|
|
724
773
|
|
|
@@ -754,13 +803,20 @@ boundary remain necessary.
|
|
|
754
803
|
|
|
755
804
|
### Statement timeout
|
|
756
805
|
|
|
757
|
-
|
|
806
|
+
SafeContext attempts a **5000ms** (5-second) timeout. Support depends on the
|
|
807
|
+
adapter and server; an unsupported setting is skipped and logged when a Rails
|
|
808
|
+
logger is available.
|
|
758
809
|
|
|
759
810
|
| Adapter | Mechanism | Scope |
|
|
760
811
|
|---------|-----------|-------|
|
|
761
|
-
| PostgreSQL | `SET statement_timeout = '5000ms'` |
|
|
762
|
-
| MySQL | `SET max_execution_time = 5000`
|
|
763
|
-
|
|
|
812
|
+
| PostgreSQL | `SET LOCAL statement_timeout = '5000ms'` | Transaction-local; discarded on rollback |
|
|
813
|
+
| MySQL | `SET max_execution_time = 5000` | SELECT only; previous session value restored in `ensure` |
|
|
814
|
+
| MariaDB | `SET max_statement_time = 5.0` | Seconds; previous session value restored in `ensure` |
|
|
815
|
+
| SQLite / unrecognized family | No supported per-statement timeout | Do not rely on a query time limit |
|
|
816
|
+
|
|
817
|
+
Rollback remains active when a timeout setting is unsupported. Recognition of a
|
|
818
|
+
MySQL-family adapter alone does not establish that its server supports the
|
|
819
|
+
corresponding timeout variable.
|
|
764
820
|
|
|
765
821
|
### SQL validation (tier 4 `console_sql`)
|
|
766
822
|
|
|
@@ -771,7 +827,7 @@ Validation runs **once**, inside the executor, with the dialect of the live adap
|
|
|
771
827
|
|
|
772
828
|
- **Allowed prefixes:** `SELECT`, `WITH...SELECT`, and plain `EXPLAIN`. `EXPLAIN ANALYZE` is rejected, it executes the query rather than just planning it (both the whitespace and `EXPLAIN (ANALYZE, …)` option-list spellings).
|
|
773
829
|
- **Rejected prefixes:** `INSERT`, `UPDATE`, `DELETE`, `MERGE`, `DROP`, `ALTER`, `TRUNCATE`, `CREATE`, `GRANT`, `REVOKE`
|
|
774
|
-
- **Rejected anywhere in query:** `UNION`, `INTO`, `COPY`; row-lock clauses (`FOR UPDATE`, `FOR NO KEY UPDATE`, `FOR SHARE`, `FOR KEY SHARE`, `FOR UPDATE NOWAIT`/`SKIP LOCKED`, MySQL `LOCK IN SHARE MODE`) — these take live row locks even inside the rolled-back transaction. The lock check is adapter-aware: `console_sql` validates with the active adapter's dialect, including MySQL double-quoted strings/backtick identifiers and PostgreSQL quoted identifiers/E-strings.
|
|
830
|
+
- **Rejected anywhere in query:** `UNION`, `INTO`, `COPY`; row-lock clauses (`FOR UPDATE`, `FOR NO KEY UPDATE`, `FOR SHARE`, `FOR KEY SHARE`, `FOR UPDATE NOWAIT`/`SKIP LOCKED`, MySQL `LOCK IN SHARE MODE`) — these take live row locks even inside the rolled-back transaction. The lock check is adapter-aware: `console_sql` validates with the active adapter's dialect, including MySQL double-quoted strings/backtick identifiers and PostgreSQL quoted identifiers/E-strings. Direct validator calls without a dialect conservatively scan all supported normalizations; packaged `console_sql` refuses unrecognized adapter families. Every view is scanned under both MySQL executable-comment (`/*!...*/`) semantics, so `#` comments and version-guarded comments cannot split a clause apart.
|
|
775
831
|
- **Function allowlist (the authoritative function control):** every function-call-shaped identifier must appear in `ALLOWED_FUNCTIONS`, a conservative set of pure read-only functions (aggregates, window functions, string/number/date/JSON readers) kept portable across MySQL, PostgreSQL, and SQLite. Anything else is rejected by name, quoted forms (`"pg_terminate_backend"(…)`) included. This is an allowlist because a denylist cannot enumerate every side-effecting function (`nextval`, `pg_advisory_lock`, `pg_terminate_backend`, …). A legacy `DANGEROUS_FUNCTIONS` denylist (`pg_sleep`, `lo_import`, `lo_export`, `pg_read_file`, `pg_write_file`, `load_file`, `sleep`, `benchmark`) still runs first as belt-and-suspenders.
|
|
776
832
|
- **Rejected patterns:** multiple statements (semicolons), writable CTEs (every `AS (...)` body is checked, so a writable CTE in any WITH position is refused — `WITH a AS (SELECT 1), b AS (DELETE FROM users RETURNING *) SELECT * FROM b`), a CTE list attached to top-level DML (`WITH a AS (SELECT 1) DELETE FROM users RETURNING *`), comment-hidden injections
|
|
777
833
|
|
|
@@ -843,6 +899,23 @@ fail closed with `Woods::ConfigurationError`.
|
|
|
843
899
|
|
|
844
900
|
The middleware lazy-initializes the MCP server on the first request, which includes `Rails.application.eager_load!`. This can take several seconds on large apps. Subsequent requests are fast. If you want to pre-warm, call a health check endpoint that touches the middleware path at app startup.
|
|
845
901
|
|
|
902
|
+
**Included in Woods 2.1:** if that eager load raises `NameError` (including a
|
|
903
|
+
Zeitwerk naming error), HTTP Console returns a stable `503` and records one
|
|
904
|
+
`console.eager_load.failed` diagnostic. That worker remains unavailable until
|
|
905
|
+
restart; it does not serve a partially populated model registry or repeatedly
|
|
906
|
+
retry loading on incoming requests. The response and diagnostic omit the raw
|
|
907
|
+
application exception. Reproduce it in the same application environment:
|
|
908
|
+
|
|
909
|
+
```bash
|
|
910
|
+
bundle exec rails runner 'Rails.application.eager_load!'
|
|
911
|
+
```
|
|
912
|
+
|
|
913
|
+
Fix the application error, then restart the Rails workers. Authentication and
|
|
914
|
+
requests outside the Console mount retain their normal behavior. Other exception
|
|
915
|
+
types retain Rails' usual failure handling. The existing stdio entry point warns
|
|
916
|
+
on eager-load `NameError` and continues; its model list may be incomplete, so fix
|
|
917
|
+
the same application error there too.
|
|
918
|
+
|
|
846
919
|
### Timeout errors on large models
|
|
847
920
|
|
|
848
921
|
The default statement timeout is 5000ms (5 seconds). If you are hitting timeouts on models with millions of rows, use `scope` to narrow the query:
|
|
@@ -902,3 +975,59 @@ These corrections require `2.0.0.beta4` or a reviewed development revision that
|
|
|
902
975
|
contains them. Confirm that a patched release is available before selecting it.
|
|
903
976
|
On affected versions, disable Console where these policies are required; Index MCP
|
|
904
977
|
can stay enabled because it reads the published code index separately.
|
|
978
|
+
|
|
979
|
+
## Console corrections included in 2.1
|
|
980
|
+
|
|
981
|
+
Woods 2.1 includes the reviewed Console corrections also released in the
|
|
982
|
+
2.0.1 and 1.6.4 security patches. For Git/path installations, verify the loaded
|
|
983
|
+
revision and gem path. Plugin updates alone do not update the Console server.
|
|
984
|
+
See the published [security advisory](https://github.com/lost-in-the/woods/security/advisories/GHSA-wxxx-6hqc-qm8g).
|
|
985
|
+
|
|
986
|
+
The patch tightens SQL policy for adapter-specific comment and quoting forms,
|
|
987
|
+
whole-row and multi-source redaction, typed key-value records, and per-mount HTTP
|
|
988
|
+
guards. Supported structured reads remain available. Raw `console_sql` requires
|
|
989
|
+
a recognized PostgreSQL, MySQL-family, or SQLite adapter; compatible subclasses
|
|
990
|
+
are recognized by ancestry. Unsupported raw SQL receives a clear refusal without
|
|
991
|
+
disabling structured tools.
|
|
992
|
+
|
|
993
|
+
Malformed HTTP origin configuration now refuses at boot with the offending entry.
|
|
994
|
+
Fix the allowlist and restart; do not relax authentication or redaction to make a
|
|
995
|
+
refused request succeed. For stdio-only use, keep HTTP disabled.
|
|
996
|
+
|
|
997
|
+
### Read policy compatibility
|
|
998
|
+
|
|
999
|
+
These rules describe Woods 2.1 and the 2.0.1 security patch. Differences from
|
|
1000
|
+
the 1.6.4 backport are called out below. Verify the installed version and, for
|
|
1001
|
+
Git/path installations, the revision and loaded gem path.
|
|
1002
|
+
|
|
1003
|
+
- **Column alias lists:** when either `console_redacted_columns` or
|
|
1004
|
+
`console_redacted_key_values` is nonempty, `console_sql` refuses relation
|
|
1005
|
+
and CTE column alias lists before execution, including lists on base tables,
|
|
1006
|
+
derived tables, parenthesized `VALUES` sources and table functions. This applies
|
|
1007
|
+
even when the selected names are not protected and independently of function
|
|
1008
|
+
validation. Ordinary relation
|
|
1009
|
+
aliases, CTEs without column lists and allowed scalar functions remain subject
|
|
1010
|
+
to the normal SQL policy. Use explicit, unaliased protected columns or a
|
|
1011
|
+
structured Console tool.
|
|
1012
|
+
- **Conservative typed masking:** EAV type lookup matches the final source-table
|
|
1013
|
+
name case-insensitively and includes every matching registered model, even
|
|
1014
|
+
across schemas. Any matching type can cause masking. This deliberately may
|
|
1015
|
+
mask extra values when table names differ only by case or share that final
|
|
1016
|
+
name; qualifying the table does not narrow that type set.
|
|
1017
|
+
- **Exact sensitive values:** `sensitive_keys` compares the stored and cast key
|
|
1018
|
+
values with exact case after string conversion. Configure their actual raw or
|
|
1019
|
+
cast spelling. `CredentialIndex` also matches credential substrings with exact
|
|
1020
|
+
case; it does not decode hexadecimal binary output such as PostgreSQL `bytea`.
|
|
1021
|
+
Binary cells have no general text-scanning guarantee: non-UTF-8 data can fail
|
|
1022
|
+
JSON normalization before scanning. Put binary secret columns in
|
|
1023
|
+
`console_redacted_columns` so they are masked before serialization.
|
|
1024
|
+
- **Adapter boundary:** raw `console_sql` requires PostgreSQL, MySQL-family
|
|
1025
|
+
(including Mysql2, MariaDB and Trilogy), or SQLite classification. Compatible
|
|
1026
|
+
subclasses are recognized by ancestry. Unknown families receive a validation
|
|
1027
|
+
refusal for raw SQL; structured tools remain available under their normal
|
|
1028
|
+
gates. See [statement timeouts](#statement-timeout) for adapter limits.
|
|
1029
|
+
- **SQL functions and select entries:** 2.x enforces a read-only function
|
|
1030
|
+
allowlist; 1.6.x retains a function denylist. For `console_query`, provide one
|
|
1031
|
+
expression per `select` array entry. 2.x refuses comma-combined entries that
|
|
1032
|
+
1.6.4 splits before validation. Execution and typed redaction use the same
|
|
1033
|
+
validated projection on all patched lines.
|
data/docs/EMBEDDING_MODELS.md
CHANGED
|
@@ -37,8 +37,8 @@ Woods.configure do |config|
|
|
|
37
37
|
model: 'bge-m3',
|
|
38
38
|
host: 'http://localhost:11434'
|
|
39
39
|
}
|
|
40
|
-
#
|
|
41
|
-
#
|
|
40
|
+
# Model context and initial chunk sizing follow the registry.
|
|
41
|
+
# Local counts are estimates; the server enforces its actual limit.
|
|
42
42
|
end
|
|
43
43
|
```
|
|
44
44
|
|
|
@@ -64,21 +64,20 @@ override. For `nomic-embed-text` (native 2048) the server rejects inputs above
|
|
|
64
64
|
that with a `400 "the input length exceeds the context length"` regardless of
|
|
65
65
|
`num_ctx`.
|
|
66
66
|
|
|
67
|
-
Woods
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
count.
|
|
67
|
+
**Included in Woods 2.1:** Woods checks complete inputs, including their
|
|
68
|
+
metadata prefixes, and splits source without discarding characters. OpenAI's
|
|
69
|
+
known byte-BPE embedding models use a conservative UTF-8 byte upper bound at
|
|
70
|
+
8191 tokens; this may create more chunks than an exact tokenizer would.
|
|
71
|
+
Ollama and custom models use explicitly labelled estimates. Ollama requests
|
|
72
|
+
set `truncate: false`, so an actual overflow fails instead of losing source.
|
|
73
|
+
A prefix that cannot fit or one unsplittable source character refuses before
|
|
74
|
+
vector/checkpoint writes; inspect the reported unit, model and limit.
|
|
75
|
+
|
|
76
|
+
Installing `tokenizers` alone no longer downloads or selects BERT for every
|
|
77
|
+
model. Advanced callers can inject a local model-matched tokenizer into
|
|
78
|
+
`Embedding::TokenCounter`; the caller must establish that it matches the server.
|
|
79
|
+
No automatic tokenizer download occurs. These checks do not prove that estimated
|
|
80
|
+
inputs fit an arbitrary tokenizer: the provider remains authoritative.
|
|
82
81
|
|
|
83
82
|
## Adding a new model to the registry
|
|
84
83
|
|
|
@@ -112,8 +111,6 @@ If you want Woods to auto-pick `num_ctx` for a model we don't ship support for:
|
|
|
112
111
|
|
|
113
112
|
- Indexing a real-world Rails app where concern-inlined models or long service
|
|
114
113
|
objects routinely exceed 2048 tokens.
|
|
115
|
-
- You already pay the `tokenizers` gem install cost and want the tighter
|
|
116
|
-
coupling between client-side verification and server-side enforcement.
|
|
117
114
|
- Multilingual content (Arctic Embed 2 is BGE M3 with a stronger non-English
|
|
118
115
|
story).
|
|
119
116
|
|