fix(devices): pair tokens are unlimited-quota (inherit from User row)
After 67225fd fixed the V2 chat 403 caused by missing SetupContextForToken,
the next probe call surfaced a new 403:
"token quota is not enough, token remain quota: \$0.000000,
need quota: \$0.001590"
Root cause: PairDevice initialised the new tokens row with
UnlimitedQuota=false and didn't set RemainQuota, so it defaulted to 0.
Every subsequent V2 chat then failed at pre-consume since the token had
no spendable budget — even though the user's actual User.Quota was
positive.
Device tokens aren't a billing boundary in our model; they're the
Ed25519 binding for a single client install. Quota belongs on the User
row. Flip UnlimitedQuota=true so the relay path consumes from
User.Quota directly, matching exactly what the legacy sk- bearer was
already doing (legacy tokens in this deployment are unlimited too).
Verified end-to-end via /tmp/v2_probe2.js after deploy: POST
/v1/messages with full V2 envelope returns HTTP 200 with the model's
reply.
This commit is contained in:
@@ -170,7 +170,15 @@ func PairDevice(c *gin.Context) {
|
||||
CreatedTime: now,
|
||||
AccessedTime: now,
|
||||
ExpiredTime: -1, // never naturally; signature validity is the gate
|
||||
UnlimitedQuota: false,
|
||||
// Device tokens are an AUTH mechanism, not a per-token billing
|
||||
// boundary — they pin a device's Ed25519 pubkey to a user row.
|
||||
// Quota / billing all belong on the User. UnlimitedQuota=true
|
||||
// means the Manager skips token-level quota gating and consumes
|
||||
// straight from User.Quota, which is what the legacy sk- bearer
|
||||
// path also did. Without this, every V2 chat returned 403
|
||||
// "token quota is not enough" because pair time defaulted
|
||||
// RemainQuota to 0.
|
||||
UnlimitedQuota: true,
|
||||
HideFromUserUI: true, // device tokens only show on Devices page
|
||||
DeviceId: &deviceIdCopy,
|
||||
DevicePubkey: &pubkeyCopy,
|
||||
|
||||
Reference in New Issue
Block a user