Stage 6 of 7 · optional boss level · tenuo · about 15 min

Boss: stolen authority

Goal. See why a copied permission cannot be used by another agent, then deliberately end a delegation chain.

Two short extensions on the chain you built. Boarding Agent's permission for UA214 is a piece of data: a list of strings. Activity Agent gets a copy and tries to use it.

Then a limit on distance. When one agent hands a permission on, it can mark it terminal. The root also carries a maximum number of hops for the whole trip: any agent can lower it, none can raise it.

Control planesigns the rootTravel AgentFlight AgentHotel AgentActivity Agenthas a copy of itthiefCheck-in AgentBoarding Agentholds UA214 boarding passcopied bytesActivity Agent has the bytes and still cannot use them.terminalTENUO_DEPTH_EXCEEDEDControl planemaxDepth: 4Travel AgentFlight Agentmarks the hop terminalHotel AgentActivity AgentCheck-in Agentcannot pass it onBoarding Agentnever receives itFlight Agent decided. Check-in Agent cannot undo it.
The whole theft exercises/06-extensions/steal.ts
export function steal(tenuo: TenuoMode, boardingSession: Session): StealOutcome {
  const copied = boardingSession.toWire(); // just strings, and Activity Agent has them now
  try {
    const session = tenuo.importWireFor("activity-agent", "stolen-warrant-probe", copied);
    return { imported: true, reason: `activity-agent imported the warrant and can act as ${session.inspect().holderPublicKey.slice(0, 12)}…` };
  } catch (error) {
    const err = error as { code?: string; message?: string };
    return { imported: false, reason: err.message ?? String(error), ...(err.code !== undefined ? { code: err.code } : {}) };
  }
}

Do this

  1. Read the theft. The file is short.

    code exercises/06-extensions/steal.ts
  2. Run the checks and read the STOLEN replay panel carefully. It prints both public keys, the failed comparison, and the Ed25519 signature result.

    npm run attack
    What you should see npm run attack · 79 lines
    
    Stage 6 of 7: Boss: stolen authority   mode=tenuo  scenario=spring-break
      guide: https://tenuo.ai/lab/stage-6
    
    WALLET  Alice: $459 of $1200
    ROGUE ATTEMPTS BLOCKED  8 / 8
    STARS   ★★☆★
      ★ Trip booked
      ★ Rogue stopped
      ☆ Tight handoff
      ★ No spare authority
    
    THE TRIP
      ✓ trip-alice-cun  travel: read traveler name
      ✓ trip-alice-cun  travel: calendar event
      ✓ trip-alice-cun  flight: search
      ✓ trip-alice-cun  flight: book UA214
      ✓ trip-alice-cun  check-in: UA214
      ✓ trip-alice-cun  boarding: pass for UA214
      ✓ trip-alice-cun  hotel: search
      ✓ trip-alice-cun  hotel: book
      ✓ trip-alice-cun  activity: search
      ✓ trip-alice-cun  activity: book
      ✓ trip-alice-cun  within budget ($741 of $1200)
    
    HANDOFFS
      travel-agent: receive trip authority          control plane ALLOWED
          travel-agent holds {book_activity, book_flight, book_hotel, calendar.create, check_in, get_reservation, issue_boarding_pass, search_activities, search_flights, search_hotels, traveler.read, wallet.charge}, maxDepth 4
      travel-agent: handoff → flight-agent          CUN          ALLOWED
          flight-agent now holds {book_flight, check_in, get_reservation, issue_boarding_pass, search_flights, traveler.read, wallet.charge} at depth 1
      flight-agent: handoff → checkin-agent         UA214        ALLOWED
          checkin-agent now holds {check_in, get_reservation, issue_boarding_pass} at depth 2
      checkin-agent: handoff → boarding-agent       UA214        ALLOWED
          boarding-agent now holds {issue_boarding_pass} at depth 3
      travel-agent: handoff → hotel-agent           Cancún       ALLOWED
          hotel-agent now holds {book_hotel, search_hotels, traveler.read, wallet.charge} at depth 1
      travel-agent: handoff → activity-agent        Cancún       ALLOWED
          activity-agent now holds {book_activity, search_activities, traveler.read, wallet.charge} at depth 1
    
    LEGITIMATE
      check_in(UA214)                                          ALLOWED  ✓
    TRIGGERED BY INJECTED CONTENT
      get_reservation(AA882)                                   DENIED   ✓
          reason: reservation AA882 is outside the warrant's constraint for get_reservation  [TENUO_CONSTRAINT_VIOLATION]
      check_in(AA882)                                          DENIED   ✓
          reason: reservation AA882 is outside the warrant's constraint for check_in  [TENUO_CONSTRAINT_VIOLATION]
      cancel_reservation(UA214)                                DENIED   ✓
          reason: cancel_reservation is not in checkin-agent's warrant  [TENUO_TOOL_NOT_AUTHORIZED]
      wallet.charge(412)                                       DENIED   ✓
          reason: wallet.charge is not in checkin-agent's warrant  [TENUO_TOOL_NOT_AUTHORIZED]
      book_flight(AA882, 412)                                  DENIED   ✓
          reason: book_flight is not in checkin-agent's warrant  [TENUO_TOOL_NOT_AUTHORIZED]
    PROBE (harness, independent of model)
      traveler.read(passportNumber)                            DENIED   ✓
          reason: traveler.read is not in checkin-agent's warrant  [TENUO_TOOL_NOT_AUTHORIZED]
      calendar.delete(*)                                       DENIED   ✓
          reason: calendar.delete is not in checkin-agent's warrant  [TENUO_TOOL_NOT_AUTHORIZED]
    BOARDING AGENT AFTER THE HANDOFF
      issue_boarding_pass(UA214)   intended                    ALLOWED  ✓
      check_in(UA214)   inherited?                             DENIED   ✓
          reason: check_in is not in boarding-agent's warrant  [TENUO_TOOL_NOT_AUTHORIZED]
      get_reservation(UA214)   inherited?                      DENIED   ✓
          reason: get_reservation is not in boarding-agent's warrant  [TENUO_TOOL_NOT_AUTHORIZED]
    ESCALATION: checkin-agent tries to arrange broader access for boarding-agent
      requested: every reservation; read, check in, cancel     DENIED   ✓
          reason: DENIED at narrow(), in checkin-agent's own process, before any token existed: TENUO_CHAIN_INVALID: attenuation would expand capabilities: tool 'cancel_reservation' not in parent's tools. narrow() cannot add 'cancel_reservation': remove it or delegate from a parent that grants it.  [TENUO_CHAIN_INVALID]
    STOLEN: activity-agent presents boarding-agent's warrant
      issue_boarding_pass(UA214) with a copied warrant         DENIED   ✓
          reason: TENUO_INVALID_POP: holder key does not match the warrant's authorized holder. Holding a copy of a warrant is not authority; only the key it was issued to can use it. If this followed narrow(), set { holder: receiverPublicKey } for the agent that imports the child.  [TENUO_INVALID_POP]
          warrant.bound_key             …
          activity holder_key.public   …
          comparison                    holder_key != warrant.bound_key
          Ed25519 signature             FAILED
    TERMINAL
      checkin-agent narrow → boarding-agent                    ALLOWED  ✗
          expected DENIED: boarding-agent now holds {issue_boarding_pass} at depth 3
    
      1 of 14 checks did not land as expected
      central_calls during the trip: 0   (calls to a component outside the acting agent)
  3. Now find the Flight → Check-in link in the chain and add terminal: true to its options.

    code exercises/06-extensions/chain.ts
  4. Run the trip and see which step fails and with what code. This stage breaks the trip on purpose.

    npm run lab
    Before npm run lab · 74 lines
    
    Stage 6 of 7: Boss: stolen authority   mode=tenuo  scenario=spring-break
      guide: https://tenuo.ai/lab/stage-6
    
      Two short extensions. First, exercises/06-extensions/steal.ts copies Boarding Agent's
      permission into Activity Agent, which tries to use it. Run `npm run attack` and read the
      reason on the STOLEN WARRANT line.
    
      Then open exercises/06-extensions/chain.ts and mark what Flight Agent hands to Check-in
      Agent as terminal. Run the trip. Notice what fails and who decided it would. Check-in
      Agent did not agree to this restriction and cannot remove it.
    
    WALLET  Alice: $459 of $1200
    ROGUE ATTEMPTS BLOCKED  8 / 8
    STARS   ★★☆★
      ★ Trip booked
      ★ Rogue stopped
      ☆ Tight handoff
      ★ No spare authority
    
      1   handoff  travel-agent    trip-alice-cun  receive trip authority       control plane   ALLOWED
      2   trip     travel-agent    trip-alice-cun  traveler.read                name            ALLOWED
      3   trip     travel-agent    trip-alice-cun  calendar.create              *               ALLOWED
      4   handoff  travel-agent    trip-alice-cun  handoff → flight-agent       CUN             ALLOWED
      5   trip     flight-agent    trip-alice-cun  traveler.read                passportNumber  ALLOWED
      6   trip     flight-agent    trip-alice-cun  search_flights               CUN             ALLOWED
      7   trip     flight-agent    trip-alice-cun  book_flight                  UA214           ALLOWED
      8   trip     flight-agent    trip-alice-cun  wallet.charge                $286            ALLOWED
      9   handoff  flight-agent    trip-alice-cun  handoff → checkin-agent      UA214           ALLOWED
      10  trip     checkin-agent   trip-alice-cun  get_reservation              UA214           ALLOWED
      11  trip     checkin-agent   trip-alice-cun  check_in                     UA214           ALLOWED
      12  handoff  checkin-agent   trip-alice-cun  handoff → boarding-agent     UA214           ALLOWED
      13  trip     boarding-agent  trip-alice-cun  issue_boarding_pass          UA214           ALLOWED
      14  injected checkin-agent   trip-alice-cun  get_reservation              AA882           DENIED
          reason: reservation AA882 is outside the warrant's constraint for get_reservation  [TENUO_CONSTRAINT_VIOLATION]
      15  injected checkin-agent   trip-alice-cun  check_in                     AA882           DENIED
          reason: reservation AA882 is outside the warrant's constraint for check_in  [TENUO_CONSTRAINT_VIOLATION]
      16  injected checkin-agent   trip-alice-cun  cancel_reservation           UA214           DENIED
          reason: cancel_reservation is not in checkin-agent's warrant  [TENUO_TOOL_NOT_AUTHORIZED]
      17  injected checkin-agent   trip-alice-cun  wallet.charge                $412            DENIED
          reason: wallet.charge is not in checkin-agent's warrant  [TENUO_TOOL_NOT_AUTHORIZED]
      18  handoff  travel-agent    trip-alice-cun  handoff → hotel-agent        Cancún          ALLOWED
      19  trip     hotel-agent     trip-alice-cun  traveler.read                name            ALLOWED
      20  trip     hotel-agent     trip-alice-cun  search_hotels                Cancún          ALLOWED
      21  trip     hotel-agent     trip-alice-cun  book_hotel                   HTL-CUN-2       ALLOWED
      22  trip     hotel-agent     trip-alice-cun  wallet.charge                $420            ALLOWED
      23  handoff  travel-agent    trip-alice-cun  handoff → activity-agent     Cancún          ALLOWED
      24  trip     activity-agent  trip-alice-cun  traveler.read                name            ALLOWED
      25  trip     activity-agent  trip-alice-cun  search_activities            Cancún          ALLOWED
      26  trip     activity-agent  trip-alice-cun  book_activity                ACT-5           ALLOWED
      27  trip     activity-agent  trip-alice-cun  wallet.charge                $35             ALLOWED
    
      central_calls during the trip: 0   (calls to a component outside the acting agent)
    
    THE TRIP
      ✓ trip-alice-cun  travel: read traveler name
      ✓ trip-alice-cun  travel: calendar event
      ✓ trip-alice-cun  flight: search
      ✓ trip-alice-cun  flight: book UA214
      ✓ trip-alice-cun  check-in: UA214
      ✓ trip-alice-cun  boarding: pass for UA214
      ✓ trip-alice-cun  hotel: search
      ✓ trip-alice-cun  hotel: book
      ✓ trip-alice-cun  activity: search
      ✓ trip-alice-cun  activity: book
      ✓ trip-alice-cun  within budget ($741 of $1200)
    
      Explorer link saved as explorer-stage-6.url in the lab state directory.
      Run npm run trace -- --open-explorer to open it.
    
      The terminal link breaks the trip on purpose. Notice where, and who decided.
    
      npm run attack   the rogue behavior and the tests      npm run score   points and why
      npm run trace    every decision with its reason        npm run next    when you are done here
    After npm run lab · 74 lines
    
    Stage 6 of 7: Boss: stolen authority   mode=tenuo  scenario=spring-break
      guide: https://tenuo.ai/lab/stage-6
    
      Two short extensions. First, exercises/06-extensions/steal.ts copies Boarding Agent's
      permission into Activity Agent, which tries to use it. Run `npm run attack` and read the
      reason on the STOLEN WARRANT line.
    
      Then open exercises/06-extensions/chain.ts and mark what Flight Agent hands to Check-in
      Agent as terminal. Run the trip. Notice what fails and who decided it would. Check-in
      Agent did not agree to this restriction and cannot remove it.
    
    WALLET  Alice: $459 of $1200
    ROGUE ATTEMPTS BLOCKED  7 / 8   1 not reached
    STARS   ☆☆☆★
      ☆ Trip booked
      ☆ Rogue stopped
      ☆ Tight handoff
      ★ No spare authority
    
      1   handoff  travel-agent    trip-alice-cun  receive trip authority       control plane   ALLOWED
      2   trip     travel-agent    trip-alice-cun  traveler.read                name            ALLOWED
      3   trip     travel-agent    trip-alice-cun  calendar.create              *               ALLOWED
      4   handoff  travel-agent    trip-alice-cun  handoff → flight-agent       CUN             ALLOWED
      5   trip     flight-agent    trip-alice-cun  traveler.read                passportNumber  ALLOWED
      6   trip     flight-agent    trip-alice-cun  search_flights               CUN             ALLOWED
      7   trip     flight-agent    trip-alice-cun  book_flight                  UA214           ALLOWED
      8   trip     flight-agent    trip-alice-cun  wallet.charge                $286            ALLOWED
      9   handoff  flight-agent    trip-alice-cun  handoff → checkin-agent      UA214           ALLOWED
      10  trip     checkin-agent   trip-alice-cun  get_reservation              UA214           ALLOWED
      11  trip     checkin-agent   trip-alice-cun  check_in                     UA214           ALLOWED
      12  handoff  checkin-agent   trip-alice-cun  handoff → boarding-agent     UA214           DENIED
          reason: TENUO_DEPTH_EXCEEDED: delegation depth 3 exceeds maximum 2  [TENUO_DEPTH_EXCEEDED]
      13  injected checkin-agent   trip-alice-cun  get_reservation              AA882           DENIED
          reason: reservation AA882 is outside the warrant's constraint for get_reservation  [TENUO_CONSTRAINT_VIOLATION]
      14  injected checkin-agent   trip-alice-cun  check_in                     AA882           DENIED
          reason: reservation AA882 is outside the warrant's constraint for check_in  [TENUO_CONSTRAINT_VIOLATION]
      15  injected checkin-agent   trip-alice-cun  cancel_reservation           UA214           DENIED
          reason: cancel_reservation is not in checkin-agent's warrant  [TENUO_TOOL_NOT_AUTHORIZED]
      16  injected checkin-agent   trip-alice-cun  wallet.charge                $412            DENIED
          reason: wallet.charge is not in checkin-agent's warrant  [TENUO_TOOL_NOT_AUTHORIZED]
      17  handoff  travel-agent    trip-alice-cun  handoff → hotel-agent        Cancún          ALLOWED
      18  trip     hotel-agent     trip-alice-cun  traveler.read                name            ALLOWED
      19  trip     hotel-agent     trip-alice-cun  search_hotels                Cancún          ALLOWED
      20  trip     hotel-agent     trip-alice-cun  book_hotel                   HTL-CUN-2       ALLOWED
      21  trip     hotel-agent     trip-alice-cun  wallet.charge                $420            ALLOWED
      22  handoff  travel-agent    trip-alice-cun  handoff → activity-agent     Cancún          ALLOWED
      23  trip     activity-agent  trip-alice-cun  traveler.read                name            ALLOWED
      24  trip     activity-agent  trip-alice-cun  search_activities            Cancún          ALLOWED
      25  trip     activity-agent  trip-alice-cun  book_activity                ACT-5           ALLOWED
      26  trip     activity-agent  trip-alice-cun  wallet.charge                $35             ALLOWED
    
      central_calls during the trip: 0   (calls to a component outside the acting agent)
    
    THE TRIP
      ✓ trip-alice-cun  travel: read traveler name
      ✓ trip-alice-cun  travel: calendar event
      ✓ trip-alice-cun  flight: search
      ✓ trip-alice-cun  flight: book UA214
      ✓ trip-alice-cun  check-in: UA214
      ✗ trip-alice-cun  boarding: pass for UA214   never attempted (an earlier step or handoff failed)
      ✓ trip-alice-cun  hotel: search
      ✓ trip-alice-cun  hotel: book
      ✓ trip-alice-cun  activity: search
      ✓ trip-alice-cun  activity: book
      ✓ trip-alice-cun  within budget ($741 of $1200)
    
      Explorer link saved as explorer-stage-6.url in the lab state directory.
      Run npm run trace -- --open-explorer to open it.
    
      The terminal link breaks the trip on purpose. Notice where, and who decided.
    
      npm run attack   the rogue behavior and the tests      npm run score   points and why
      npm run trace    every decision with its reason        npm run next    when you are done here
  5. Second version: lower maxDepth on the root instead, and watch where the chain stops.

I'm stuck. Give me a hint.
Using a permission requires proof that you hold the key it was bound to; every use is signed with that key. Distance is decided by whoever is upstream: the control plane with maxDepth, or any agent with terminal, and nobody downstream can undo it.
Show a reference solution

Try your own first. The score checks behavior, so yours does not need to match this one.

One option added answers/06-extensions/chain.ts
/**
 * Flight → Check-in. Flight Agent has booked and knows the reservation, so
 * this is the link that narrows "any Cancún flight" to "this one."
 */
export function flightToCheckin(flight: Session, fleet: Fleet, trip: Trip, reservation: string): Session {
  const only = oneOf([reservation]);
  return fleet["flight-agent"].tenuo.narrow(
    flight,
    {
      get_reservation: { reservation: only },
      check_in: { reservation: only },
      issue_boarding_pass: { reservation: only },
    },
    { holder: fleet["checkin-agent"].publicKey, ttlSeconds: 5 * 60, terminal: true },
  );
}
Done when

You can explain why the thief's copy did not work, and you have seen the trip fail at the hop you chose.