#!/usr/bin/env python3
"""Run a built T3 server in a fresh home; capture real HTTP refusals without secrets.
Usage: python3 prove.py CHECKOUT OUTPUT_PARENT SIDE NON_LOOPBACK_IP
Build first: (cd CHECKOUT/apps/server && vp run build:bundle)
No production database, Connect account, or external relay is used.
"""
import sys, pathlib, subprocess, os, time, re, json, urllib.request, urllib.parse, urllib.error, datetime, textwrap
checkout, parent, side, address = sys.argv[1:]
out = pathlib.Path(parent) / (side + '-' + datetime.datetime.now(datetime.timezone.utc).strftime('%Y%m%dT%H%M%SZ'))
out.mkdir(parents=True)
port = 18326
sha = subprocess.check_output(['git','-C',checkout,'rev-parse','HEAD'],text=True).strip()
log = out / 'private-server.log'
home = pathlib.Path(checkout) / ('.t3-proof-' + out.name)
env = dict(os.environ, T3CODE_DEV_SEED='0')
server = subprocess.Popen(['node','apps/server/dist/bin.mjs','serve','--host','0.0.0.0','--port',str(port),'--base-dir',str(home),'--no-browser'],cwd=checkout,env=env,stdout=log.open('w'),stderr=subprocess.STDOUT)
start = time.monotonic(); events=[]; lines=[]
def emit(s):
    for line in s.splitlines():
        lines.extend(textwrap.wrap(line, 100, replace_whitespace=False) or [''])
    events.append([round(time.monotonic()-start,3),'o',s+'\r\n'])
    print(s,flush=True)
def post(base,path,data,headers=None,form=False):
    body = urllib.parse.urlencode(data).encode() if form else json.dumps(data).encode()
    req=urllib.request.Request(base+path,data=body,headers={'Content-Type':'application/x-www-form-urlencoded' if form else 'application/json',**(headers or {})})
    try: r=urllib.request.build_opener(urllib.request.ProxyHandler({})).open(req)
    except urllib.error.HTTPError as e: r=e
    return r.status,json.loads(r.read())
try:
    deadline=time.monotonic()+90
    while time.monotonic()<deadline:
        raw=log.read_text()
        token=re.search(r'^Token: (\S+)',raw,re.M)
        if token: break
        if server.poll() is not None: raise RuntimeError('Server exited; inspect private log')
        time.sleep(.2)
    else: raise RuntimeError('Server did not become ready')
    emit('T3 Code real server | '+side.upper()+' | '+sha)
    emit('Environment: Linux x64, built server, fresh empty database, unlinked.')
    emit('Transport: HTTP port 18326; non-loopback interface on the same VM.')
    emit('Capture: chronological runtime transcript; credentials and interface IP omitted.')
    emit('POST /oauth/token [startup pairing credential; never printed]')
    status,auth=post('http://127.0.0.1:18326','/oauth/token',{'grant_type':'urn:ietf:params:oauth:grant-type:token-exchange','subject_token':token.group(1),'subject_token_type':'urn:t3:params:oauth:token-type:environment-bootstrap','requested_token_type':'urn:ietf:params:oauth:token-type:access_token'},form=True)
    emit('HTTP '+str(status)+'; authenticated paired-client bearer session issued.')
    bearer=auth['access_token']
    payload={'challenge':'evidence-challenge','relayIssuer':'https://relay.example.test','endpoint':{'providerKind':'cloudflare_tunnel','httpBaseUrl':'https://environment.example.test','wsBaseUrl':'wss://environment.example.test'},'origin':{'localHttpHost':'127.0.0.1','localHttpPort':port}}
    for label,base,headers in [('Direct non-loopback authority','http://'+address+':18326',{}),('Forwarded host authority','http://127.0.0.1:18326',{'x-forwarded-host':'environment.example.test'}),('Forwarded protocol authority','http://127.0.0.1:18326',{'x-forwarded-proto':'https'})]:
        emit('\n'+label+' -> POST /api/connect/link-proof')
        emit('Payload: tunnel endpoint environment.example.test; origin 127.0.0.1:18326.')
        if headers: emit('Headers: '+json.dumps(headers))
        status,response=post(base,'/api/connect/link-proof',payload,{'Authorization':'Bearer '+bearer,**headers})
        emit('HTTP '+str(status))
        emit(json.dumps(response,ensure_ascii=False))
    emit('\nCompleted. No relay contacted; all three requests refused.')
finally:
    server.send_signal(2)
    try: server.wait(timeout=15)
    except subprocess.TimeoutExpired: server.terminate(); server.wait(timeout=10)
(out/'transcript.txt').write_text('\n'.join(lines)+'\n')
with (out/'recording.cast').open('w') as f:
    f.write(json.dumps({'version':2,'width':104,'height':40,'timestamp':int(time.time()),'env':{'TERM':'xterm-256color'},'title':'T3 Connect HTTP runtime evidence'})+'\n')
    for event in events: f.write(json.dumps(event,ensure_ascii=False)+'\n')
print('Saved evidence to '+str(out))
