otto 2.9.0 → 2.11.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/.github/dependabot.yml +5 -0
- data/.github/workflows/ci.yml +11 -8
- data/.github/workflows/claude-code-review.yml +38 -13
- data/.github/workflows/claude.yml +10 -8
- data/.github/workflows/code-smells.yml +5 -5
- data/.github/workflows/release-gem.yml +2 -2
- data/.github/workflows/ruby-lint.yml +3 -3
- data/.github/workflows/yardoc.yml +5 -5
- data/.gitignore +1 -5
- data/.rubocop_todo.yml +8 -5
- data/AGENTS.md +40 -1
- data/CHANGELOG.rst +235 -0
- data/Gemfile +3 -3
- data/Gemfile.lock +11 -15
- data/README.md +75 -37
- data/docs/README.md +166 -0
- data/docs/adr/README.md +16 -0
- data/docs/adr/adr-001-route-authentication-at-handler-boundary.md +38 -0
- data/docs/adr/adr-002-multi-strategy-authentication-and-authorization.md +50 -0
- data/docs/adr/adr-003-caddy-tls-route-based-integration.md +48 -0
- data/docs/adr/adr-004-separate-compatibility-from-security-maintenance.md +58 -0
- data/docs/guides/authentication.md +377 -0
- data/docs/guides/caddy-tls.md +205 -0
- data/docs/guides/configuration_freezing.md +157 -0
- data/docs/guides/enrichment.md +161 -0
- data/docs/guides/forwarded-authority.md +249 -0
- data/docs/guides/geo-country.md +168 -0
- data/docs/guides/ip_privacy.md +39 -0
- data/docs/guides/ipaddr-encoding-quirk.md +56 -0
- data/docs/guides/mcp.md +282 -0
- data/docs/guides/privacy.md +193 -0
- data/docs/guides/routing.md +293 -0
- data/docs/guides/structured_logging.md +281 -0
- data/docs/guides/testing-guide.md +391 -0
- data/docs/maintainers/github-actions.md +41 -0
- data/docs/maintainers/investigations/.gitignore +2 -0
- data/docs/migrating/v2.0.0.md +337 -0
- data/docs/reference/authentication.md +290 -0
- data/docs/reference/route-syntax.md +181 -0
- data/docs/reference/runtime-and-dependency-security.md +86 -0
- data/examples/advanced_routes/README.md +43 -57
- data/examples/authentication_strategies/README.md +37 -196
- data/examples/basic/README.md +24 -39
- data/examples/basic/config.ru +0 -1
- data/examples/caddy_tls_demo/README.md +8 -2
- data/examples/lambda_handlers/README.md +11 -2
- data/examples/mcp_demo/README.md +75 -161
- data/examples/mcp_demo/config.ru +1 -0
- data/examples/security_features/README.md +36 -234
- data/lib/otto/caddy_tls/localhost_guard.rb +22 -24
- data/lib/otto/core/configuration.rb +40 -23
- data/lib/otto/core/error_handler.rb +40 -2
- data/lib/otto/core/file_safety.rb +105 -31
- data/lib/otto/core/middleware_stack.rb +36 -36
- data/lib/otto/core/router.rb +94 -18
- data/lib/otto/core/static_mounts.rb +172 -0
- data/lib/otto/core.rb +1 -0
- data/lib/otto/env_keys.rb +20 -2
- data/lib/otto/mcp/auth/token.rb +10 -4
- data/lib/otto/mcp/core.rb +23 -5
- data/lib/otto/mcp/endpoint.rb +41 -0
- data/lib/otto/mcp/errors.rb +15 -0
- data/lib/otto/mcp/options.rb +292 -0
- data/lib/otto/mcp/protocol.rb +52 -22
- data/lib/otto/mcp/rate_limiting.rb +175 -97
- data/lib/otto/mcp/registry.rb +14 -14
- data/lib/otto/mcp/schema_validation.rb +20 -12
- data/lib/otto/mcp/server.rb +131 -32
- data/lib/otto/optional_dependency.rb +57 -0
- data/lib/otto/privacy/config.rb +10 -8
- data/lib/otto/security/authentication/route_auth_wrapper/role_authorization.rb +22 -5
- data/lib/otto/security/authentication/strategies/api_key_strategy.rb +181 -18
- data/lib/otto/security/authentication/strategies/permission_strategy.rb +1 -0
- data/lib/otto/security/authentication/strategies/role_strategy.rb +1 -0
- data/lib/otto/security/authentication/strategies/session_strategy.rb +1 -0
- data/lib/otto/security/authentication/strategy_result.rb +58 -28
- data/lib/otto/security/config.rb +328 -14
- data/lib/otto/security/configurator.rb +36 -6
- data/lib/otto/security/core.rb +19 -1
- data/lib/otto/security/middleware/ip_privacy_middleware.rb +105 -4
- data/lib/otto/security/middleware/rate_limit_middleware.rb +1 -6
- data/lib/otto/security/rate_limiter.rb +81 -46
- data/lib/otto/static.rb +38 -0
- data/lib/otto/utils.rb +50 -0
- data/lib/otto/version.rb +1 -1
- data/lib/otto.rb +130 -13
- data/otto.gemspec +0 -2
- metadata +32 -41
- data/docs/.gitignore +0 -10
- data/docs/1108-STREAMING_ARCHITECTURE_ANALYSIS.md +0 -1105
- data/docs/1108-STREAMING_SUPPORT_SUMMARY.md +0 -376
- data/docs/enrichment.md +0 -128
- data/docs/geo-country.md +0 -181
- data/docs/ipaddr-encoding-quirk.md +0 -34
- data/docs/migrating/v2.0.0-pre1.md +0 -276
- data/docs/migrating/v2.0.0-pre2.md +0 -338
- data/docs/modern-authentication-authorization-landscape.md +0 -558
- data/docs/multi-strategy-authentication-design.md +0 -1401
- data/docs/reverse-proxy-network-services.md +0 -371
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b8303453d265fcf2d5b0fa0754aafdbdb3e24eb89be9dc5a7b5611b4f2bcc699
|
|
4
|
+
data.tar.gz: 2081706f65a3475f95555d96547841691f906b015f8b012739abc7804923fc76
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 75c496ee29c9c4ec0b05a8effda77e4e1dafc02080fb341a2320313011437f2b0bc0511ad6ca7d1b53928d6f75130693c974c72ed053aa35e5c6d16f60af0a5e
|
|
7
|
+
data.tar.gz: 9d9615efa73c7ebafafe7f7e1eb3e098f8c07f3200ac62882c52c39954087c73053fc9574beda1b1e11ecc836e0b9bacb70fe267c90fa92600531ef14fca1b63
|
data/.github/dependabot.yml
CHANGED
|
@@ -9,6 +9,11 @@ updates:
|
|
|
9
9
|
directory: "/" # Location of package manifests
|
|
10
10
|
schedule:
|
|
11
11
|
interval: "weekly"
|
|
12
|
+
# All action bumps arrive as one PR instead of one per action.
|
|
13
|
+
groups:
|
|
14
|
+
github-actions:
|
|
15
|
+
patterns:
|
|
16
|
+
- "*"
|
|
12
17
|
- package-ecosystem: "bundler"
|
|
13
18
|
directory: "/" # Location of package manifests
|
|
14
19
|
schedule:
|
data/.github/workflows/ci.yml
CHANGED
|
@@ -28,15 +28,18 @@ jobs:
|
|
|
28
28
|
fail-fast: false
|
|
29
29
|
matrix:
|
|
30
30
|
# Each Ruby runs twice: once against the committed Gemfile.lock
|
|
31
|
-
# (
|
|
32
|
-
#
|
|
33
|
-
# gemspec
|
|
34
|
-
#
|
|
35
|
-
# releases that satisfy `~> X.Y` but break Otto at load time -
|
|
31
|
+
# (the reproducible maintainer snapshot) and once with the lockfile
|
|
32
|
+
# removed so Bundler resolves currently available versions inside the
|
|
33
|
+
# gemspec constraints. Neither resolution is a patched security floor;
|
|
34
|
+
# consumers must audit their own lockfiles. The unlocked cells catch
|
|
35
|
+
# upstream releases that satisfy `~> X.Y` but break Otto at load time -
|
|
36
36
|
# e.g. facets 3.2.0 shipping a self-referential
|
|
37
37
|
# `require_relative 'file/write.rb'` against a file deleted in
|
|
38
38
|
# the same release, the reason 2.0.2 exists.
|
|
39
39
|
include:
|
|
40
|
+
# Ruby 3.2 is EOL upstream but remains a blocking compatibility
|
|
41
|
+
# target. Ruby 3.5 and 4.0 remain provisional, non-blocking targets.
|
|
42
|
+
# See docs/reference/runtime-and-dependency-security.md.
|
|
40
43
|
- ruby: "3.2"
|
|
41
44
|
experimental: false
|
|
42
45
|
lockfile: "locked"
|
|
@@ -69,9 +72,9 @@ jobs:
|
|
|
69
72
|
lockfile: "unlocked"
|
|
70
73
|
|
|
71
74
|
steps:
|
|
72
|
-
- uses: actions/checkout@v7.0.1
|
|
75
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
73
76
|
- name: Set up Ruby
|
|
74
|
-
uses: ruby/setup-ruby@v1
|
|
77
|
+
uses: ruby/setup-ruby@95ef2b042f9d7a56d8268cba8559e2842e2ad01b # v1.321.0
|
|
75
78
|
continue-on-error: ${{ matrix.experimental }}
|
|
76
79
|
with:
|
|
77
80
|
ruby-version: ${{ matrix.ruby }}
|
|
@@ -80,7 +83,7 @@ jobs:
|
|
|
80
83
|
bundler-cache: ${{ !matrix.experimental && matrix.lockfile == 'locked' }}
|
|
81
84
|
|
|
82
85
|
- name: Setup tmate session
|
|
83
|
-
uses: mxschmitt/action-tmate@35b54afac29c97fb54faba5b513f8fbd1882f113 # v3
|
|
86
|
+
uses: mxschmitt/action-tmate@35b54afac29c97fb54faba5b513f8fbd1882f113 # v3.24
|
|
84
87
|
if: ${{ github.event_name == 'workflow_dispatch' && inputs.debug_enabled }}
|
|
85
88
|
with:
|
|
86
89
|
detached: true
|
|
@@ -30,13 +30,20 @@ jobs:
|
|
|
30
30
|
# CLAUDE_CODE_OAUTH_TOKEN is empty and the action would fail.
|
|
31
31
|
# 3. The event is a freshly opened PR, a 'claude-review' label add, or a push
|
|
32
32
|
# to a PR that already carries the 'claude-review' label.
|
|
33
|
+
# A manual workflow_dispatch run bypasses 2 and 3 (it can only be started by a
|
|
34
|
+
# user with write access, on a branch in this repo) but still skips bots.
|
|
33
35
|
if: ${{
|
|
34
36
|
!endsWith(github.actor, '[bot]') &&
|
|
35
|
-
!github.event.pull_request.head.repo.fork &&
|
|
36
37
|
(
|
|
37
|
-
|
|
38
|
-
(
|
|
39
|
-
|
|
38
|
+
github.event_name == 'workflow_dispatch' ||
|
|
39
|
+
(
|
|
40
|
+
!github.event.pull_request.head.repo.fork &&
|
|
41
|
+
(
|
|
42
|
+
(github.event.action == 'opened') ||
|
|
43
|
+
(github.event.action == 'labeled' && github.event.label.name == 'claude-review') ||
|
|
44
|
+
(github.event.action == 'synchronize' && contains(github.event.pull_request.labels.*.name, 'claude-review'))
|
|
45
|
+
)
|
|
46
|
+
)
|
|
40
47
|
)
|
|
41
48
|
}}
|
|
42
49
|
|
|
@@ -48,13 +55,13 @@ jobs:
|
|
|
48
55
|
|
|
49
56
|
steps:
|
|
50
57
|
- name: Checkout repository
|
|
51
|
-
uses: actions/checkout@v7.0.1
|
|
58
|
+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
52
59
|
with:
|
|
53
60
|
fetch-depth: 1
|
|
54
61
|
|
|
55
62
|
- name: Run Claude Code Review
|
|
56
63
|
id: claude-review
|
|
57
|
-
uses: anthropics/claude-code-action@
|
|
64
|
+
uses: anthropics/claude-code-action@19dda84776b3518d98b8798e591daee763049ed3 # v1.0.220
|
|
58
65
|
with:
|
|
59
66
|
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
|
|
60
67
|
|
|
@@ -62,11 +69,26 @@ jobs:
|
|
|
62
69
|
# runs) -> the CLAUDE_MODEL repo variable -> this built-in default.
|
|
63
70
|
# Pinning a current id avoids the action's frozen default, which 404s
|
|
64
71
|
# ("model: claude-sonnet-4-20250514"). Fall back to Sonnet on overload.
|
|
65
|
-
|
|
66
|
-
|
|
72
|
+
# v1 removed the model/fallback_model inputs; both are now CLI flags
|
|
73
|
+
# passed through claude_args.
|
|
74
|
+
claude_args: |
|
|
75
|
+
--model ${{ inputs.model || vars.CLAUDE_MODEL || 'claude-opus-4-6' }}
|
|
76
|
+
--fallback-model ${{ vars.CLAUDE_FALLBACK_MODEL || 'claude-sonnet-4-6' }}
|
|
77
|
+
|
|
78
|
+
# Prompt for automated review (no @claude mention needed).
|
|
79
|
+
# v1 renamed direct_prompt -> prompt. track_progress restores the
|
|
80
|
+
# tracking/sticky comment that v0 automation mode posted by default; it
|
|
81
|
+
# only applies to pull_request/issue events, so a workflow_dispatch run
|
|
82
|
+
# has no PR to comment on and reports to the job log instead.
|
|
83
|
+
track_progress: true
|
|
84
|
+
prompt: |
|
|
85
|
+
REPO: ${{ github.repository }}
|
|
86
|
+
PR NUMBER: ${{ github.event.pull_request.number || 'none (manual run)' }}
|
|
87
|
+
|
|
88
|
+
If no PR number is given, use `gh` to find an open pull request whose
|
|
89
|
+
head is ${{ github.ref_name }}; if there is none, say so and stop.
|
|
90
|
+
(The checkout is shallow, so compare via the API, not local git.)
|
|
67
91
|
|
|
68
|
-
# Direct prompt for automated review (no @claude mention needed)
|
|
69
|
-
direct_prompt: |
|
|
70
92
|
Please review this pull request and provide feedback on:
|
|
71
93
|
- Code quality and best practices
|
|
72
94
|
- Potential bugs or issues
|
|
@@ -80,9 +102,12 @@ jobs:
|
|
|
80
102
|
use_sticky_comment: true
|
|
81
103
|
|
|
82
104
|
- name: Remove claude-review label
|
|
83
|
-
# Remove label whether success or failure - prevents getting stuck
|
|
84
|
-
|
|
85
|
-
|
|
105
|
+
# Remove label whether success or failure - prevents getting stuck.
|
|
106
|
+
# Only for pull_request events: a workflow_dispatch payload has no
|
|
107
|
+
# issue number, so context.issue.number would be undefined and the
|
|
108
|
+
# removeLabel call would fail (silently, below) with the label intact.
|
|
109
|
+
if: always() && github.event_name == 'pull_request' && github.event.action != 'opened'
|
|
110
|
+
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
|
86
111
|
with:
|
|
87
112
|
script: |
|
|
88
113
|
try {
|
|
@@ -26,13 +26,13 @@ jobs:
|
|
|
26
26
|
actions: read # Required for Claude to read CI results on PRs
|
|
27
27
|
steps:
|
|
28
28
|
- name: Checkout repository
|
|
29
|
-
uses: actions/checkout@v7.0.1
|
|
29
|
+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
30
30
|
with:
|
|
31
31
|
fetch-depth: 1
|
|
32
32
|
|
|
33
33
|
- name: Run Claude Code
|
|
34
34
|
id: claude
|
|
35
|
-
uses: anthropics/claude-code-action@
|
|
35
|
+
uses: anthropics/claude-code-action@19dda84776b3518d98b8798e591daee763049ed3 # v1.0.220
|
|
36
36
|
with:
|
|
37
37
|
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
|
|
38
38
|
|
|
@@ -40,8 +40,11 @@ jobs:
|
|
|
40
40
|
# editing this file. Pinning a current id avoids the action's frozen
|
|
41
41
|
# default, which 404s ("model: claude-sonnet-4-20250514"). Fall back to
|
|
42
42
|
# Sonnet if the primary is unavailable or overloaded.
|
|
43
|
-
model
|
|
44
|
-
|
|
43
|
+
# v1 removed the model/fallback_model inputs; both are now CLI flags
|
|
44
|
+
# passed through claude_args.
|
|
45
|
+
claude_args: |
|
|
46
|
+
--model ${{ vars.CLAUDE_MODEL || 'claude-opus-4-6' }}
|
|
47
|
+
--fallback-model ${{ vars.CLAUDE_FALLBACK_MODEL || 'claude-sonnet-4-6' }}
|
|
45
48
|
|
|
46
49
|
# This is an optional setting that allows Claude to read CI results on PRs
|
|
47
50
|
additional_permissions: |
|
|
@@ -50,7 +53,6 @@ jobs:
|
|
|
50
53
|
# Optional: Give a custom prompt to Claude. If this is not specified, Claude will perform the instructions specified in the comment that tagged it.
|
|
51
54
|
# prompt: 'Update the pull request description to include a summary of changes.'
|
|
52
55
|
|
|
53
|
-
#
|
|
54
|
-
#
|
|
55
|
-
# or https://docs.anthropic.com/en/docs/claude-code/sdk#command-line
|
|
56
|
-
# claude_args: '--model claude-opus-4-1-20250805 --allowed-tools Bash(gh pr:*)'
|
|
56
|
+
# More claude_args options (tool allowlists, --max-turns, --mcp-config):
|
|
57
|
+
# https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md
|
|
58
|
+
# or https://docs.anthropic.com/en/docs/claude-code/sdk#command-line
|
|
@@ -21,10 +21,10 @@ jobs:
|
|
|
21
21
|
|
|
22
22
|
steps:
|
|
23
23
|
- name: Checkout code
|
|
24
|
-
uses: actions/checkout@v7.0.1
|
|
24
|
+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
25
25
|
|
|
26
26
|
- name: Set up Ruby
|
|
27
|
-
uses: ruby/setup-ruby@v1
|
|
27
|
+
uses: ruby/setup-ruby@95ef2b042f9d7a56d8268cba8559e2842e2ad01b # v1.321.0
|
|
28
28
|
with:
|
|
29
29
|
ruby-version: 3.4
|
|
30
30
|
|
|
@@ -71,7 +71,7 @@ jobs:
|
|
|
71
71
|
continue-on-error: true
|
|
72
72
|
|
|
73
73
|
- name: Upload Reek report as artifact
|
|
74
|
-
uses: actions/upload-artifact@v7
|
|
74
|
+
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
75
75
|
if: always()
|
|
76
76
|
with:
|
|
77
77
|
name: reek-report
|
|
@@ -88,10 +88,10 @@ jobs:
|
|
|
88
88
|
|
|
89
89
|
steps:
|
|
90
90
|
- name: Checkout code
|
|
91
|
-
uses: actions/checkout@v7.0.1
|
|
91
|
+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
92
92
|
|
|
93
93
|
- name: Set up Ruby
|
|
94
|
-
uses: ruby/setup-ruby@v1
|
|
94
|
+
uses: ruby/setup-ruby@95ef2b042f9d7a56d8268cba8559e2842e2ad01b # v1.321.0
|
|
95
95
|
with:
|
|
96
96
|
ruby-version: 3.4
|
|
97
97
|
bundler-cache: true
|
|
@@ -139,7 +139,7 @@ jobs:
|
|
|
139
139
|
persist-credentials: false
|
|
140
140
|
|
|
141
141
|
- name: Set up Ruby
|
|
142
|
-
uses: ruby/setup-ruby@
|
|
142
|
+
uses: ruby/setup-ruby@95ef2b042f9d7a56d8268cba8559e2842e2ad01b # v1.321.0
|
|
143
143
|
with:
|
|
144
144
|
bundler-cache: true
|
|
145
145
|
# Pinned to 3.3 (oldest non-experimental Ruby in the CI matrix).
|
|
@@ -169,4 +169,4 @@ jobs:
|
|
|
169
169
|
echo "Releasing otto ${gem_version} from tag ${RELEASE_TAG}"
|
|
170
170
|
|
|
171
171
|
- name: Build and push gem to RubyGems
|
|
172
|
-
uses: rubygems/release-gem@
|
|
172
|
+
uses: rubygems/release-gem@7f9650160c1a4e7989fdc9855807bdbd421d8b6b # v1.4.1
|
|
@@ -44,15 +44,15 @@ jobs:
|
|
|
44
44
|
|
|
45
45
|
steps:
|
|
46
46
|
- name: Checkout code
|
|
47
|
-
uses: actions/checkout@v7.0.1
|
|
47
|
+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
48
48
|
|
|
49
|
-
- uses: ruby/setup-ruby@v1
|
|
49
|
+
- uses: ruby/setup-ruby@95ef2b042f9d7a56d8268cba8559e2842e2ad01b # v1.321.0
|
|
50
50
|
with:
|
|
51
51
|
ruby-version: ${{ matrix.ruby }}
|
|
52
52
|
bundler-cache: true
|
|
53
53
|
|
|
54
54
|
- name: Setup tmate session
|
|
55
|
-
uses: mxschmitt/action-tmate@35b54afac29c97fb54faba5b513f8fbd1882f113 # v3
|
|
55
|
+
uses: mxschmitt/action-tmate@35b54afac29c97fb54faba5b513f8fbd1882f113 # v3.24
|
|
56
56
|
if: ${{ github.event_name == 'workflow_dispatch' && inputs.debug_enabled }}
|
|
57
57
|
with:
|
|
58
58
|
detached: true
|
|
@@ -42,12 +42,12 @@ jobs:
|
|
|
42
42
|
|
|
43
43
|
steps:
|
|
44
44
|
- name: Checkout repository
|
|
45
|
-
uses: actions/checkout@v7.0.1
|
|
45
|
+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
46
46
|
with:
|
|
47
47
|
fetch-depth: 0
|
|
48
48
|
|
|
49
49
|
- name: Set up Ruby environment
|
|
50
|
-
uses: ruby/setup-ruby@v1
|
|
50
|
+
uses: ruby/setup-ruby@95ef2b042f9d7a56d8268cba8559e2842e2ad01b # v1.321.0
|
|
51
51
|
with:
|
|
52
52
|
ruby-version: '3.4'
|
|
53
53
|
bundler-cache: true
|
|
@@ -82,10 +82,10 @@ jobs:
|
|
|
82
82
|
echo "::endgroup::"
|
|
83
83
|
|
|
84
84
|
- name: Setup GitHub Pages configuration
|
|
85
|
-
uses: actions/configure-pages@v6
|
|
85
|
+
uses: actions/configure-pages@45bfe0192ca1faeb007ade9deae92b16b8254a0d # v6.0.0
|
|
86
86
|
|
|
87
87
|
- name: Upload documentation artifact
|
|
88
|
-
uses: actions/upload-pages-artifact@v5
|
|
88
|
+
uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5.0.0
|
|
89
89
|
with:
|
|
90
90
|
path: './doc'
|
|
91
91
|
|
|
@@ -102,7 +102,7 @@ jobs:
|
|
|
102
102
|
steps:
|
|
103
103
|
- name: Deploy to GitHub Pages
|
|
104
104
|
id: deployment
|
|
105
|
-
uses: actions/deploy-pages@v5
|
|
105
|
+
uses: actions/deploy-pages@368f82528645a54fb793d4d04e342629a3f51346 # v5.0.1
|
|
106
106
|
|
|
107
107
|
notify-completion:
|
|
108
108
|
timeout-minutes: 5
|
data/.gitignore
CHANGED
|
@@ -1,19 +1,14 @@
|
|
|
1
1
|
.DS_Store
|
|
2
2
|
.bundle
|
|
3
|
-
.byebug*
|
|
4
3
|
.history
|
|
5
4
|
.devcontainer
|
|
6
5
|
.vscode
|
|
7
6
|
*.env
|
|
8
7
|
*.log
|
|
9
|
-
*.md
|
|
10
8
|
*.txt
|
|
11
9
|
.*.json
|
|
12
10
|
!LICENSE.txt
|
|
13
11
|
!spec/fixtures/*.txt
|
|
14
|
-
!examples/**/*.md
|
|
15
|
-
!README.md
|
|
16
|
-
!CLAUDE.md
|
|
17
12
|
.ruby-version
|
|
18
13
|
appendonlydir
|
|
19
14
|
data/
|
|
@@ -27,3 +22,4 @@ vendor
|
|
|
27
22
|
.mcp.json
|
|
28
23
|
.claude
|
|
29
24
|
.serena
|
|
25
|
+
.yardoc
|
data/.rubocop_todo.yml
CHANGED
|
@@ -296,15 +296,11 @@ Lint/DuplicateMethods:
|
|
|
296
296
|
Exclude:
|
|
297
297
|
- 'lib/otto/privacy/geo_resolver.rb'
|
|
298
298
|
|
|
299
|
-
# Offense count:
|
|
299
|
+
# Offense count: 1
|
|
300
300
|
# Configuration parameters: AllowedParentClasses.
|
|
301
301
|
Lint/MissingSuper:
|
|
302
302
|
Exclude:
|
|
303
303
|
- 'lib/otto/mcp/rate_limiting.rb'
|
|
304
|
-
- 'lib/otto/security/authentication/strategies/api_key_strategy.rb'
|
|
305
|
-
- 'lib/otto/security/authentication/strategies/permission_strategy.rb'
|
|
306
|
-
- 'lib/otto/security/authentication/strategies/role_strategy.rb'
|
|
307
|
-
- 'lib/otto/security/authentication/strategies/session_strategy.rb'
|
|
308
304
|
|
|
309
305
|
# Offense count: 12
|
|
310
306
|
# This cop supports unsafe autocorrection (--autocorrect-all).
|
|
@@ -601,6 +597,9 @@ RSpec/DescribeMethod:
|
|
|
601
597
|
- 'spec/otto/initialization_spec.rb'
|
|
602
598
|
- 'spec/otto/locale_config_spec.rb'
|
|
603
599
|
- 'spec/otto/mcp/rate_limiting_spec.rb'
|
|
600
|
+
- 'spec/otto/mcp/protocol_spec.rb'
|
|
601
|
+
- 'spec/otto/mcp/registry_spec.rb'
|
|
602
|
+
- 'spec/otto/mcp/route_parser_spec.rb'
|
|
604
603
|
- 'spec/otto/mcp_route_parsing_spec.rb'
|
|
605
604
|
- 'spec/otto/rate_limiting_spec.rb'
|
|
606
605
|
- 'spec/otto/response_integration_spec.rb'
|
|
@@ -860,9 +859,13 @@ RSpec/SpecFilePathFormat:
|
|
|
860
859
|
- 'spec/otto/enhanced_routing_spec.rb'
|
|
861
860
|
- 'spec/otto/error_handler_registration_spec.rb'
|
|
862
861
|
- 'spec/otto/error_handling_spec.rb'
|
|
862
|
+
- 'spec/otto/fallback_response_isolation_spec.rb'
|
|
863
863
|
- 'spec/otto/file_safety_spec.rb'
|
|
864
864
|
- 'spec/otto/locale_config_spec.rb'
|
|
865
865
|
- 'spec/otto/mcp/rate_limiting_spec.rb'
|
|
866
|
+
- 'spec/otto/mcp/protocol_spec.rb'
|
|
867
|
+
- 'spec/otto/mcp/registry_spec.rb'
|
|
868
|
+
- 'spec/otto/mcp/route_parser_spec.rb'
|
|
866
869
|
- 'spec/otto/mcp/token_auth_spec.rb'
|
|
867
870
|
- 'spec/otto/mcp_route_parsing_spec.rb'
|
|
868
871
|
- 'spec/otto/rate_limiting_spec.rb'
|
data/AGENTS.md
CHANGED
|
@@ -2,6 +2,27 @@
|
|
|
2
2
|
|
|
3
3
|
This file provides essential guidance to AI agents when working with Otto.
|
|
4
4
|
|
|
5
|
+
## Project claims and source authority
|
|
6
|
+
|
|
7
|
+
### Attribution
|
|
8
|
+
|
|
9
|
+
Do not infer project terminology or guarantees from repetition. Before attributing a claim to the project, locate an authoritative primary source and provide its exact wording. Treat delivery notes, commit messages, agent output, and documents created or modified during the current task as leads, not evidence. If the wording is absent, call it an interpretation or proposal. Never place paraphrases in quotation marks.
|
|
10
|
+
|
|
11
|
+
### Authoritative sources
|
|
12
|
+
|
|
13
|
+
Authoritative sources must be identified explicitly; repository presence alone does not confer authority. Accepted specifications and ADRs may establish project claims only within their stated scope. Delivery notes, commit messages, issue discussions, summaries, and agent-authored text are non-authoritative unless an authoritative source incorporates them explicitly.
|
|
14
|
+
|
|
15
|
+
### Normative claims
|
|
16
|
+
|
|
17
|
+
For normative claims concerning security, privacy, compatibility, persistence, or data loss:
|
|
18
|
+
|
|
19
|
+
1. Cite the authoritative source and its exact wording.
|
|
20
|
+
2. Distinguish quotations, paraphrases, interpretations, and proposals.
|
|
21
|
+
3. Do not use material created or modified during the current task to validate that task’s claims.
|
|
22
|
+
4. If no authoritative wording exists, report the claim as unsupported.
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
5
26
|
## Error Handler Registration
|
|
6
27
|
|
|
7
28
|
Register handlers for expected business logic errors to avoid logging them as 500 errors:
|
|
@@ -48,6 +69,24 @@ Helper modules should avoid overriding these methods inherited from Rack::Reques
|
|
|
48
69
|
|
|
49
70
|
No runtime validation is performed for performance reasons. Overriding these methods will cause undefined behavior.
|
|
50
71
|
|
|
72
|
+
## Static File Registration
|
|
73
|
+
|
|
74
|
+
Files under the `public:` directory are served without registration. Use
|
|
75
|
+
`mount_static` to bind a URL prefix to a directory outside it, or to verify a
|
|
76
|
+
required asset directory at boot:
|
|
77
|
+
|
|
78
|
+
```ruby
|
|
79
|
+
otto = Otto.new('routes.txt', public: 'public')
|
|
80
|
+
otto.mount_static('/assets', root: 'build/assets')
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
- Roots are canonicalized at registration; a missing or unsafe root raises `ArgumentError`
|
|
84
|
+
- Precedence is fixed: literal routes, then mounts (longest prefix first), then `public:`, then dynamic routes
|
|
85
|
+
- Must be registered before first request (before configuration freezing)
|
|
86
|
+
- `add_static_path` was removed in v2.10.0 and has no shim
|
|
87
|
+
|
|
88
|
+
See `docs/guides/routing.md` for the full contract.
|
|
89
|
+
|
|
51
90
|
## Authentication Architecture
|
|
52
91
|
|
|
53
92
|
Authentication is handled by `RouteAuthWrapper` at the handler level, NOT by middleware.
|
|
@@ -56,7 +95,7 @@ Authentication is handled by `RouteAuthWrapper` at the handler level, NOT by mid
|
|
|
56
95
|
|
|
57
96
|
```ruby
|
|
58
97
|
otto.add_auth_strategy('session', SessionStrategy.new)
|
|
59
|
-
otto.add_auth_strategy('apikey', APIKeyStrategy.new)
|
|
98
|
+
otto.add_auth_strategy('apikey', APIKeyStrategy.new(api_keys: ENV.fetch('API_KEYS').split(',')))
|
|
60
99
|
```
|
|
61
100
|
|
|
62
101
|
- Strategy names must be unique
|