Summary
The public interface, implementation, and protobuf define RemoveRoutes as a deletion returning Empty, but the gRPC handler calls findRoutes and returns a routes-shaped object. The actual deletion method is never invoked.
Impact: A successful RemoveRoutes RPC leaves the AOR's routes in the store. Subsequent lookups can continue routing to an endpoint the caller believed was deregistered.
Code path
Checked against current main at commit bf3de25a401d2313538677486400864354ff5edf.
mods/location/src/utils.ts:79-87
mods/location/src/location.ts:138-140
mods/common/src/protos/location.proto:32-35
Steps to reproduce
Validation level: current HEAD supplemental dynamic witness reproduced.
node -e 'const fs=require("fs"),source=fs.readFileSync("mods/location/src/utils.ts","utf8"); function compile(name,locator){const marker=name+": async (call: CT.GrpcCall, callback) => ",start=source.indexOf(marker)+marker.length; let depth=0,end=-1; for(let i=start;i<source.length;i++){if(source[i]==="{")depth++; if(source[i]==="}"&&--depth===0){end=i;break}} const body=source.slice(start,end+1).replace("call.request as FindRoutesRequest","call.request"); return new Function("locator","return async (call, callback) => "+body)(locator)} let findCalls=0,removeCalls=0,callbackResult; const locator={findRoutes:async()=>{findCalls++;return[{host:"still-registered"}]},removeRoutes:async()=>{removeCalls++}}; compile("removeRoutes",locator)({request:{aor:"sip:alice@example.test"}},(err,value)=>{callbackResult={err:err??null,value}}).then(()=>console.log(JSON.stringify({findCalls,removeCalls,callbackResult})))'
Observed output:
{"findCalls":1,"removeCalls":0,"callbackResult":{"err":null,"value":{"routes":[{"host":"still-registered"}]}}}
Neighboring control:
node -e 'const fs=require("fs"),source=fs.readFileSync("mods/location/src/utils.ts","utf8"); function compile(name,locator){const marker=name+": async (call: CT.GrpcCall, callback) => ",start=source.indexOf(marker)+marker.length; let depth=0,end=-1; for(let i=start;i<source.length;i++){if(source[i]==="{")depth++; if(source[i]==="}"&&--depth===0){end=i;break}} const body=source.slice(start,end+1).replace(/call\.request as \w+/g,"call.request"); return new Function("locator","return async (call, callback) => "+body)(locator)} let addCalls=0,findCalls=0,callbackResult; const locator={addRoute:async()=>{addCalls++},findRoutes:async()=>{findCalls++;return[]}}; compile("addRoute",locator)({request:{aor:"sip:alice@example.test"}},(err,value)=>{callbackResult={err:err??null,value}}).then(()=>console.log(JSON.stringify({addCalls,findCalls,callbackResult})))'
Control output:
{"addCalls":1,"findCalls":0,"callbackResult":{"err":null,"value":{}}}
Expected behavior
The documented/source-grounded contract should hold without the drift described above.
Actual behavior
The RemoveRoutes handler performs findRoutes, never invokes deletion, and returns route data despite the protobuf Empty contract.
Existing coverage
I checked the current issue and PR lists for overlapping titles/root-cause keywords before filing this. I did not find an item covering this same root cause.
Suggested fix
Dispatch the RemoveRoutes gRPC handler to locator.removeRoutes and return an Empty response.
Suggested tests
- Add a regression test for the reproduction above.
- Add a neighboring control assertion so the intended non-bug path remains covered.
Submitted with Codex.
Summary
The public interface, implementation, and protobuf define RemoveRoutes as a deletion returning Empty, but the gRPC handler calls findRoutes and returns a routes-shaped object. The actual deletion method is never invoked.
Impact: A successful RemoveRoutes RPC leaves the AOR's routes in the store. Subsequent lookups can continue routing to an endpoint the caller believed was deregistered.
Code path
Checked against current
mainat commitbf3de25a401d2313538677486400864354ff5edf.mods/location/src/utils.ts:79-87mods/location/src/location.ts:138-140mods/common/src/protos/location.proto:32-35Steps to reproduce
Validation level: current HEAD supplemental dynamic witness reproduced.
node -e 'const fs=require("fs"),source=fs.readFileSync("mods/location/src/utils.ts","utf8"); function compile(name,locator){const marker=name+": async (call: CT.GrpcCall, callback) => ",start=source.indexOf(marker)+marker.length; let depth=0,end=-1; for(let i=start;i<source.length;i++){if(source[i]==="{")depth++; if(source[i]==="}"&&--depth===0){end=i;break}} const body=source.slice(start,end+1).replace("call.request as FindRoutesRequest","call.request"); return new Function("locator","return async (call, callback) => "+body)(locator)} let findCalls=0,removeCalls=0,callbackResult; const locator={findRoutes:async()=>{findCalls++;return[{host:"still-registered"}]},removeRoutes:async()=>{removeCalls++}}; compile("removeRoutes",locator)({request:{aor:"sip:alice@example.test"}},(err,value)=>{callbackResult={err:err??null,value}}).then(()=>console.log(JSON.stringify({findCalls,removeCalls,callbackResult})))'Observed output:
Neighboring control:
node -e 'const fs=require("fs"),source=fs.readFileSync("mods/location/src/utils.ts","utf8"); function compile(name,locator){const marker=name+": async (call: CT.GrpcCall, callback) => ",start=source.indexOf(marker)+marker.length; let depth=0,end=-1; for(let i=start;i<source.length;i++){if(source[i]==="{")depth++; if(source[i]==="}"&&--depth===0){end=i;break}} const body=source.slice(start,end+1).replace(/call\.request as \w+/g,"call.request"); return new Function("locator","return async (call, callback) => "+body)(locator)} let addCalls=0,findCalls=0,callbackResult; const locator={addRoute:async()=>{addCalls++},findRoutes:async()=>{findCalls++;return[]}}; compile("addRoute",locator)({request:{aor:"sip:alice@example.test"}},(err,value)=>{callbackResult={err:err??null,value}}).then(()=>console.log(JSON.stringify({addCalls,findCalls,callbackResult})))'Control output:
Expected behavior
The documented/source-grounded contract should hold without the drift described above.
Actual behavior
The RemoveRoutes handler performs findRoutes, never invokes deletion, and returns route data despite the protobuf Empty contract.
Existing coverage
I checked the current issue and PR lists for overlapping titles/root-cause keywords before filing this. I did not find an item covering this same root cause.
Suggested fix
Dispatch the RemoveRoutes gRPC handler to locator.removeRoutes and return an Empty response.
Suggested tests
Submitted with Codex.