Repository navigation
Support for differently named contracts. - #161
Conversation
AI disclosure: Used GLM-5.3 for planning and code generation.
|
/review AI review started. |
Advanced AI Review
Click to expand reviewI'll review this PR thoroughly. Let me start by examining the relevant source files.Let me check the auth middleware and a few remaining details.Now let me verify the tests actually pass. First checking the test setup:Deno isn't available in this environment, so I'll complete a thorough static review. Let me examine the remaining pieces:Let me check how contract manifests/sources get deployed (to assess the new size checks) and whether the dashboard uses I reviewed the diff against the full source (routes, workers, db-utils, auth, config, tests). I could not execute 1. 🟡 Size-cap checks run before the cheap
|
| for (const hash of contractSourceHashes) { | ||
| const source = await sbp('chelonia.db/get', hash) | ||
| if (typeof source !== 'string') throw new HTTPException(422, { message: 'Missing contract source' }) | ||
| contractSizeBytes += Buffer.byteLength(source) | ||
| } | ||
| if (contractSizeBytes > nconf.get('server:signup:maxContractSizeBytes')) { | ||
| throw new HTTPException(413, { message: 'Contract source exceeds size limit' }) |
There was a problem hiding this comment.
These added as per @taoeffect's request. It doesn't seem useful to me to check for maxContractSizeBytes, as the contract source size is not a concern here (contract sources are uploaded by a different mechanism).
| let contractSizeBytes = 0 | ||
| for (const hash of contractSourceHashes) { | ||
| const source = await sbp('chelonia.db/get', hash) | ||
| if (typeof source !== 'string') throw new HTTPException(422, { message: 'Missing contract source' }) | ||
| contractSizeBytes += Buffer.byteLength(source) | ||
| } |
There was a problem hiding this comment.
Ordering question. The manifest read and these source reads now happen before
the signup.disabled check (line 398) and before the rate limiter below it.
So on a server with signup.disabled = true, every request still does one DB
read for the manifest plus up to maxContractSizeBytes of source reads, 500 KB
by default, before it returns 403. Same for an IP that is already over the rate
limit, it pays that cost on every attempt.
Before this PR the only work ahead of those two checks was reading the manifest
and pulling the name out of it? Now it is bounded by config, but the bound is
500 KB per request.
Can signup.disabled and the rate limiter move above the manifest read? The
maxFirstMessageBytes check at line 373 can stay where it is, the payload is
already in memory so it costs nothing.
There was a problem hiding this comment.
Good point. I think we could move those pre-existing checks to happen before these (like in my other comment, I also think we don't need the contract source checks)
|
/review AI review started. |
Advanced AI Review
Click to expand reviewDeno isn't available here, so I'll do a thorough static review. Let me examine the remaining relevant files.The nconf semantics matter for the test helpers. Let me check nconf's actual source online.# Review: Support for differently named contracts I reviewed the full diff ( 1. 🟡 README claims the signup limits bound unpaid storage per client — they don't
Note that charges are currently recorded but not enforced: a billable entity
whose balance goes negative keeps being served. Since each accepted
registration creates an entity with its own free allowance, the signup limits
above are what bounds how much unpaid storage a single client can accumulate.The final sentence is contradicted by the sentence right before it. Only the first message of a contract is size-capped ( Suggested rewording: Note that charges are currently recorded but not enforced: a billable entity
whose balance goes negative keeps being served. Until enforcement is added,
the signup limits bound only how many free allowances a client can claim —
not how much unpaid storage a single registration can accumulate, since
messages other than the first are neither size-capped nor rate-limited.(Minor grammar note in the same sentence: "are what bounds" → "are what bound", though rewording as above makes it moot.) 2. 🟡
|
|
LGTM. README.md issues can be addressed in #164 |
52b3069 to
39064b1
Compare
taoeffect
left a comment
There was a problem hiding this comment.
Great work @corrideat! Need a docs update to merge!
akhileshthite
left a comment
There was a problem hiding this comment.
Tested this against our TodoMVC. Renamed our contract from
gi.contracts/identity to todomvc/identity, dropped the workaround, and ran
the suite against a local build of this branch: all 35 e2e tests pass,
including signup, username registration and login on a browser that has never
seen the account.
The same rename against the published 3.4.0 fails every signup with a 401, so
the tests really are exercising this change and not just passing anyway.
|
@corrideat Review by Opus 5.5 ready! DetailsCode ReviewBase: Context: #160. The 1. 🟡 Per-IP signup limits reset after ~5 idle minutes, so the hourly and daily caps are not enforced
So once an IP exhausts its hourly quota, its The limiter config is the same as on master. This branch makes it load-bearing, though:
Each registration is now worth 10 MiB of free storage, and any contract name qualifies. const group = (reservoir: number, intervalMs: number): Bottleneck.Group => {
return new Bottleneck.Group({
strategy: Bottleneck.strategy.LEAK,
highWater: 0,
reservoir,
reservoirRefreshInterval: intervalMs,
reservoirRefreshAmount: reservoir,
// Keep a key alive for its whole window; the default (5 min) silently
// resets the hourly/daily buckets
timeout: intervalMs
})
}Trade-off: the 250 ms per-key heartbeats then live for up to a day per distinct IP. A plain 2. 🟡 The default
|
Closes #160
AI disclosure: Used GLM-5.3 for planning and code generation.