Quando a alocação de TTY no SSH muda um comando?
A alocação de TTY no SSH muda prompts, fluxos, sinais e detecção do terminal. Saiba quando usar -T, -t ou -tt sem quebrar a automação.

Adicionar -t a um comando SSH faz muito mais do que exibir um prompt. A opção troca três fluxos remotos separados por um terminal, instala as regras de linha do terminal, atribui um terminal de controle à sessão e leva programas a se comportarem como se uma pessoa estivesse olhando. Isso pode corrigir uma execução interativa do sudo enquanto quebra silenciosamente um analisador, um pipeline ou o cancelamento.
Para tarefas sem supervisão, o padrão deve ser não usar PTY. Solicite um apenas quando o programa remoto realmente precisar da semântica de terminal e trate essa escolha como parte do contrato do comando. Já vi muitos scripts de implantação ganharem -tt durante uma emergência e mantê-lo por anos, muito depois de todos esquecerem qual falha ele escondia.
Um PTY muda o ambiente do processo
A alocação de TTY no SSH muda os descritores de arquivo que o processo remoto recebe, não apenas a aparência da saída. Sem PTY, o OpenSSH conecta entrada padrão, saída padrão e erro padrão por pipes ou pares de sockets. Com PTY, o OpenSSH o torna o terminal de controle e duplica o mesmo descritor de terminal nos três fluxos padrão.
Esse detalhe de implementação produz consequências visíveis. Um programa pode chamar isatty() e escolher um caminho interativo. O driver do terminal pode processar caracteres especiais de entrada, repetir a entrada, converter finais de linha e acompanhar um grupo de processos em primeiro plano. O erro padrão deixa de ter um canal remoto independente porque os dois descritores de saída apontam para o mesmo terminal.
O RFC 4254 mantém esses conceitos separados no protocolo. Uma sessão pode solicitar pty-req e depois pedir um shell ou um comando exec. A solicitação de PTY inclui tipo de terminal, dimensões e modos de terminal codificados. Nada nessa solicitação transforma um pedido exec em shell de login.
O OpenSSH oferece três formas úteis de fazer a escolha:
ssh -T host commanddesativa explicitamente a alocação de PTY.ssh -t host commandsolicita um PTY quando o cliente local tem um terminal.ssh -tt host commandforça a solicitação mesmo quando a entrada local vem de um pipe ou de outra fonte que não seja terminal.
O -t duplicado não cria um terminal remoto mais poderoso. Ele ignora a verificação de segurança local. A diferença importa em um executor de build, um processo de agente ou printf ... | ssh, onde um único -t pode imprimir “Pseudo-terminal will not be allocated because stdin is not a terminal” e continuar sem a semântica esperada pelo autor.
Não usar PTY também preserva um caminho transparente para bytes. O manual ssh(1) do OpenSSH considera esse modo apropriado para transferir dados binários com confiabilidade. Um PTY é um dispositivo de caracteres sujeito a regras de processamento, portanto é o transporte errado para um arquivo compactado, um dump de banco de dados ou uma saída para máquinas cujos bytes precisam permanecer exatos.
A mesma escolha pode estar escondida na configuração. A opção de cliente RequestTTY do OpenSSH aceita no, yes, force ou auto, correspondentes aos comportamentos normalmente acessados por -T, -t e pelo padrão. Verifique a configuração efetiva com ssh -G host.example quando um comando recebe um terminal sem solicitá-lo na linha de comando. Blocos de host e arquivos incluídos podem transformar dois comandos aparentemente idênticos em sessões diferentes.
O servidor também decide. PermitTTY pode negar a alocação, e uma entrada de authorized key pode ter a restrição no-pty. Um comando forçado só roda em um PTY quando o cliente o solicita e o servidor permite. Trate a recusa como uma incompatibilidade real da interface, e não como motivo para tentar novamente com mais opções -t.
Meça os dois modos antes de mudar
Uma pequena sonda revela mais do que outra rodada de adivinhação sobre opções do SSH. Execute-a com a mesma conta, configuração de host e caminho de comando usados em produção:
probe='
for fd in 0 1 2; do
if test -t "$fd"; then kind=tty; else kind=not-tty; fi
printf "fd%s=%s\n" "$fd" "$kind"
done
printf "term=%s\n" "${TERM-unset}"
printf "stdout-marker\n"
printf "stderr-marker\n" >&2
exit 23
'
ssh -T host.example "$probe" >no-pty.out 2>no-pty.err
printf 'no-pty ssh status=%s\n' "$?"
ssh -tt host.example "$probe" >pty.out 2>pty.err
printf 'pty ssh status=%s\n' "$?"
A execução sem terminal deve colocar as três linhas fdN=not-tty, term=unset e stdout-marker em no-pty.out. Ela deve colocar apenas stderr-marker em no-pty.err. O resultado exato de TERM pode variar porque clientes e servidores aceitam configurações de ambiente de formas diferentes, então registre o que sua rota real fornece em vez de depender do exemplo.
A execução com PTY deve informar os três descritores como tty. Os dois marcadores normalmente chegam a pty.out, muitas vezes com finais de linha formados por retorno de carro e nova linha. pty.err contém diagnósticos locais do SSH, se houver, mas não consegue recuperar o erro padrão original do programa remoto como fluxo separado. Os dois comandos SSH devem retornar 23 porque a alocação de PTY sozinha não descarta o status de saída remoto.
Amplie a sonda com o executável real antes de mudar uma tarefa. Adicione command -V tool, pwd, uma impressão filtrada do ambiente e as opções de diagnóstico sem segredos do programa. Se a saída mudar, identifique qual observação provocou o desvio: detecção de terminal, TERM, largura do terminal, fluxos mesclados, arquivo de inicialização ou prompt. “Funciona com -t” é apenas um relato do sintoma.
Execute a sonda pelo mesmo inicializador. Um comando colado em um shell tem TTY local; um agente ou trabalhador de integração contínua normalmente não tem. Por isso um experimento com -t no teclado pode divergir da execução automática até você testar -tt, e por isso forçar um PTY pode expor uma tarefa a entradas que ela nunca esperou.
Teste a entrada separadamente da saída. ssh -n redireciona a entrada padrão do cliente a partir de /dev/null, e a opção StdinNull faz o mesmo pela configuração. Isso ajuda quando um comando remoto nunca deve consumir a entrada do chamador, mas não desativa a alocação de PTY por si só. Um PTY forçado com entrada nula ainda muda a detecção de terminal, a mesclagem dos fluxos e o comportamento na desconexão.
Tenha cuidado ao executar SSH dentro de um loop. Sem -n, o primeiro comando remoto pode ler linhas destinadas ao loop, mesmo que o processo remoto não pretenda consumi-las. Com PTY, o eco do terminal também pode devolver esses bytes à transcrição. Defina quem controla a entrada padrão antes de interpretar qualquer outra diferença.
Prompts do sudo precisam de um contrato explícito
O sudo precisa de um meio de autenticação, e um PTY oferece um terminal para ler a senha. O manual do sudo afirma que normalmente lê a senha no terminal do usuário. Sem um terminal, a exigência de senha falha, a menos que exista um auxiliar askpass ou o chamador escolha outro método de entrada.
Isso explica a correção conhecida:
ssh -t host.example 'sudo systemctl restart example.service'
Ela é aceitável para uma pessoa que pretende digitar uma senha e observar o comando. É uma correção ruim para execução sem supervisão. A tarefa pode esperar para sempre, um prompt pode contaminar a saída esperada e alguém pode acabar enviando uma senha pelo mesmo canal dos dados do comando.
Use sudo não interativo na automação:
ssh -T host.example \
'sudo -n /usr/bin/systemctl restart example.service'
A opção -n diz ao sudo para não perguntar. Se credenciais em cache ou a política sudoers não autorizarem a ação sem interação, o sudo termina com erro. Essa falha é útil: o chamador recebe um status definido no lugar de um prompt oculto. Combine-a com uma regra sudoers limitada ao comando e aos argumentos exatos quando a automação privilegiada for realmente necessária. Permitir um shell inteiro sem senha troca um problema de disponibilidade por outro de autoridade muito maior.
Não faça uma tarefa sem supervisão depender de um timestamp do sudo em cache. O cache pertence a um contexto de autenticação cujos detalhes de terminal e sessão variam conforme a política. Uma tarefa que funciona porque um administrador usou sudo um minuto antes e falha depois de um fim de semana tranquilo não tem um projeto de autorização; ela teve sorte com o momento.
sudo -S lê a senha da entrada padrão. Ele pode fazer um comando sem PTY funcionar, mas mistura um segredo ao caminho de dados e entrega o gerenciamento da senha ao chamador. Evite-o para agentes e automação de rotina. Um auxiliar askpass pode servir a um fluxo gráfico acompanhado por uma pessoa, mas ainda é autenticação interativa e não deve ser improvisado em uma tarefa supostamente autônoma.
Dois PTYs costumam ser confundidos aqui. O SSH pode alocar um PTY remoto antes de iniciar o sudo. Separadamente, versões modernas do sudo podem executar o comando autorizado em seu próprio PTY para registro de E/S e isolamento; o sudo 1.9.14 habilita use_pty por padrão. Esse PTY interno não fornece o prompt externo de senha quando a sessão SSH não tem terminal. Ele começa depois que o sudo já tem contexto suficiente para executar o comando.
Algumas configurações de sudoers também exigem terminal como parte da política. Configurações empresariais antigas usavam requiretty com frequência, e um sistema atual ainda pode mantê-lo. Confirme a política sudoers efetiva em vez de presumir que toda mensagem “terminal is required” indica um prompt de senha.
Um PTY não escolhe arquivos de inicialização do shell
Um comando SSH com PTY continua sendo executado pelo shell configurado na conta por meio de -c. O manual sshd(8) do OpenSSH diz que um comando solicitado é executado assim, e o código do servidor o entrega ao shell do usuário. A alocação de PTY muda os descritores ao redor desse shell; não adiciona -i, -l nem uma opção de login.
Isso importa quando PATH, um gerenciador de linguagens ou um alias existe no prompt interativo, mas não na automação. Adicionar -t pode parecer corrigir o caminho porque um arquivo de inicialização contém testes de terminal ou porque a ferramenta chamada detecta o terminal. Também pode não fazer nada. Depender disso vincula a tarefa a arquivos que os desenvolvedores editam para seus próprios terminais.
O Bash tem um caso especial que torna essa crença mais difícil de esclarecer. Seu manual afirma que um shell de login interativo lê arquivos de perfil, um shell interativo sem login lê .bashrc e um shell não interativo usa BASH_ENV. O Bash também tenta reconhecer quando é executado por um daemon de shell remoto e pode ler .bashrc mesmo sem interação, a menos que seja chamado como sh. Esse é um comportamento do Bash, não uma promessa do SSH, e não é portável para todo shell de conta.
Selecione o modo do shell diretamente quando precisar:
# A predictable noninteractive Bash command with explicit inputs
ssh -T host.example \
'PATH=/usr/local/bin:/usr/bin:/bin /bin/bash -c '\''command -v deploy && deploy'\'''
# A login environment, requested because the command truly depends on it
ssh -T host.example \
'/bin/bash -lc '\''command -v deploy && deploy'\'''
A segunda forma lê de propósito os arquivos de inicialização de login e herda seus riscos: uma saída impressa por um perfil pode corromper um protocolo, e um perfil pode mudar o comportamento sem revisão da implantação. Para tarefas de produção, um caminho absoluto do executável e um ambiente pequeno e explícito costumam ser melhores.
Aliases são outra armadilha. O Bash não interativo não expande aliases a menos que expand_aliases esteja habilitado, e uma função de shell só existe se algum mecanismo de inicialização a definir ou importar. Se ssh host deploy só funciona depois que você adiciona -t, confirme que deploy é um executável real. Um terminal não deve decidir se a conta remota consegue localizar o programa.
Lembre que o SSH envia uma string de comando, não um vetor de argumentos para o executável final. O shell da conta remota analisa essa string depois que o shell local já processou suas próprias aspas. A alocação de PTY não muda nenhuma das duas análises. Quando valores puderem conter espaços ou metacaracteres do shell, envie um script com entradas fixas, use um auxiliar remoto com formato de dados definido ou proteja cada camada deliberadamente. Um terminal não pode corrigir um argumento que o shell local expandiu cedo demais.
Ctrl-C segue regras de terminal apenas com PTY
Com PTY, Ctrl-C normalmente é um byte de entrada interpretado pelo driver do terminal remoto. O POSIX o chama de caractere INTR. Quando a opção ISIG está ativa, o driver descarta esse byte e envia SIGINT a todos os processos no grupo de primeiro plano do terminal. Por isso um pipeline remoto em primeiro plano pode parar como uma única tarefa.
O cliente SSH local coloca o terminal local em modo adequado e envia as teclas pelo canal. O PTY remoto controla entrada canônica, eco, caracteres especiais e tamanho da janela. Programas como editores de tela cheia podem mudar esses modos, e uma desconexão anormal pode deixar a tela local com aparência errada até que o terminal seja redefinido.
Sem PTY, não existe driver de terminal remoto para interpretar Ctrl-C nem grupo de primeiro plano do terminal. O RFC 4254 define uma solicitação separada de canal SSH signal, mas permite que sistemas sem implementação de sinais a ignorem. Portanto, cliente, servidor, wrapper e programa chamado podem produzir um cancelamento diferente do terminal interativo.
Ctrl-Z e o controle de tarefas revelam a diferença ainda mais rápido. Em um terminal, o caractere SUSP pode enviar SIGTSTP ao grupo em primeiro plano, e um shell interativo pode retomar a tarefa depois. Um comando remoto de execução única não ganha uma conversa útil de controle de tarefas apenas por ter PTY. Suspendê-lo pode deixar o cliente SSH esperando por um grupo remoto parado, sem prompt de shell para executar fg.
Não considere uma transação remota segura por presumir que o operador conseguirá apertar Ctrl-C na hora certa. Coloque a semântica de cancelamento no comando remoto. Um wrapper de shell pode capturar sinais e terminar o grupo de filhos, um gerenciador de serviços pode assumir o processo e um tempo limite remoto pode restringir a execução. Depois teste a desconexão, não apenas o teclado.
ssh -tt host.example '
trap '\''printf "wrapper got INT\n" >&2; exit 130'\'' INT
printf "remote shell pid=%s\n" "$$"
sleep 300
'
Pressionar Ctrl-C deve exercitar o caminho PTY e devolver o controle. Repita com o programa real porque shells, aplicativos de terminal e supervisores instalam handlers diferentes. Teste também o fechamento da conexão de rede. A desmontagem do PTY pode gerar comportamento de hangup para a sessão de controle, enquanto um filho sem PTY que conserva um pipe de saída pode manter o canal SSH aberto. Colocar um processo em segundo plano não é um plano de separação em nenhum dos modos.
Para tarefas longas, envie-as a um gerenciador de serviços ou sistema de jobs remoto e retorne um identificador. Assim o cancelamento ganha um alvo remoto explícito, e o SSH informa apenas se o envio funcionou. Isso também impede que uma breve interrupção do cliente decida se metade de uma migração continuará.
O status de saída sobrevive, mas wrappers podem substituí-lo
O OpenSSH retorna o status de saída do comando remoto nos dois modos. Seu manual de cliente diz que ssh termina com esse status ou com 255 quando ocorre um erro. O RFC 4254 descreve a mensagem de canal exit-status como um valor sem sinal de 32 bits e define uma mensagem exit-signal separada para encerramento por sinal.
A alocação de PTY não torna um status de sucesso mais confiável. A composição do shell determina qual status se torna o do comando remoto. Este comando informa o status de printf, portanto pode esconder uma implantação com falha:
ssh -tt host.example 'deploy; printf "finished\n"'
Preserve o resultado de propósito:
ssh -T host.example '
deploy
rc=$?
printf "deploy_status=%s\n" "$rc" >&2
exit "$rc"
'
rc=$?
printf 'ssh_status=%s\n' "$rc"
exit "$rc"
Pipelines locais podem escondê-lo novamente. Em shells sem uma opção de status do pipeline, ssh host command | tee log geralmente retorna o status de tee. Capture primeiro a saída SSH em um arquivo, use um shell com um recurso de status do pipeline testado ou leia os status de cada comando. Não presuma que set -e corrige todo pipeline, condição ou subshell.
O status 255 é ambíguo porque o OpenSSH o reserva para erros do cliente, enquanto um programa remoto também pode sair com 255. Se a distinção importar, faça o wrapper remoto emitir um registro estruturado de conclusão em um canal protegido ou mapeie os status da aplicação para uma faixa combinada. Falha de autenticação, falha na verificação do host, perda do transporte e um 255 remoto literal não devem provocar a mesma nova tentativa.
Falhas na abertura da conexão ocorrem antes que o wrapper remoto possa imprimir qualquer coisa. Use tempos limite do cliente e diagnósticos SSH suficientes para classificá-las localmente, mas mantenha esses diagnósticos fora do arquivo de dados do programa. Quando o servidor iniciar o wrapper, inclua um identificador de execução e um registro de status final para que o chamador distinga “nunca iniciou” de “iniciou e depois perdeu a conexão”. Essa distinção evita repetir às cegas comandos que talvez já tenham alterado o estado.
Encerramentos por sinal exigem o mesmo cuidado. Um shell costuma representar um sinal como 128 mais o número do sinal, mas o protocolo SSH pode informar um sinal de saída diretamente, e o comportamento do cliente não precisa parecer com o status de um filho de shell local em todos os casos. Defina os status que seu wrapper emite em vez de tentar interpretar todos os valores acima de 128 depois de um incidente.
Um PTY mistura fluxos e altera bytes
Solicitar um PTY abre mão da separação limpa entre saída padrão e erro padrão remotos. A implementação de servidor do OpenSSH duplica o slave do PTY nos descritores 0, 1 e 2. O protocolo tem um canal de dados estendido para erro padrão, mas um processo ligado a um único terminal já escreveu os dois fluxos na mesma sequência de bytes.
Isso quebra um padrão comum de automação:
ssh -T host.example command >result.json 2>diagnostic.log
Sem PTY, o erro padrão do programa remoto pode chegar a diagnostic.log enquanto o JSON permanece em result.json. Com PTY, avisos remotos, prompts de senha, banners, exibições de progresso e JSON podem chegar juntos a result.json; diagnósticos locais do SSH ainda podem aparecer em diagnostic.log. O redirecionamento no cliente não consegue separar bytes depois que o servidor os misturou.
O processamento de saída do terminal também pode converter nova linha em retorno de carro mais nova linha. A entrada pode ser repetida, o modo canônico pode esperar uma linha inteira e caracteres de controle podem acionar funções do terminal. Esses comportamentos são corretos para um terminal e representam corrupção em um protocolo binário.
Programas que reconhecem terminal costumam adicionar cor, barras de progresso, paginadores ou prompts. Eles podem usar buffer por linha no terminal e por bloco no pipe, fazendo uma execução sem PTY parecer parada mesmo enquanto trabalha. Corrija o buffer no aplicativo quando possível: escolha sua opção de saída simples, modo sem buffer ou configuração de log. Um terminal falso muda várias condições de uma vez e pode esconder o verdadeiro problema.
Se um comando produz dados para máquinas, mantenha -T e faça o programa emitir um formato não interativo. Se ele realmente precisa de terminal, trate toda a saída como transcrição. Não analise uma transcrição de PTY como se fosse uma API estável.
O SSH executado por agentes deve falhar sem conversar
Um chamador autônomo não consegue controlar com segurança uma conversa de terminal. Um prompt que ajuda uma pessoa a se recuperar pode fazer um agente esperar, improvisar uma entrada ou classificar uma ação parcial como sucesso. O comando SSH deve declarar se a interação é permitida antes que credenciais, privilégios ou estado remoto entrem em cena.
Em execuções de agentes, combine a ausência de PTY com a supressão de prompts em cada camada. Autenticação e verificação do host SSH precisam de uma política predefinida. A elevação de privilégio deve usar sudo -n. A ferramenta remota deve receber sua opção não interativa, um tempo limite explícito e entradas por arquivos ou argumentos nomeados, em vez de uma conversa. Capture separadamente a saída padrão, o erro padrão e o status.
Não corrija a ausência de um prompt de senha expondo uma senha ou chave privada ao agente. Material de autenticação e comportamento do terminal são assuntos separados. Dar um PTY ao processo não reduz o que credenciais roubadas podem fazer, e negar o PTY não protege credenciais que já estão no ambiente.
O Sallyport tira a fronteira das credenciais do processo do agente: um agente compatível com MCP solicita uma ação SSH pelo auxiliar sem estado sp-ssh, enquanto o cofre criptografado fornece a chave SSH e o diário Activity registra a chamada. O comando ainda precisa de um contrato de PTY intencional, pois isolar segredos não decide se o programa remoto espera semântica de terminal.
A aprovação também é diferente de um prompt remoto. Um cartão de aprovação local pode autorizar uma ação definida antes da execução; um prompt de terminal remoto aparece dentro de uma sessão aberta e pode surgir depois de efeitos anteriores. Mantenha a autorização humana fora do fluxo remoto de bytes para que uma recusa tenha significado claro e os logs identifiquem a ação tentada.
Quando um agente precisar de uma ferramenta interativa de manutenção, divida o fluxo. Deixe o agente preparar um comando ou pedido e depois entregue a sessão de terminal ao vivo a uma pessoa, ou troque a ferramenta por uma operação não interativa feita para automação. Fingir que um agente é digitador produz a versão menos testável dos dois sistemas.
Escolha o modo como parte da interface
O modo correto vem da interface do programa remoto, e não de uma regra geral de que PTYs são bons ou ruins. Use esta lista durante a revisão:
- Use
-Tpara JSON, arquivos e bytes exatos porque ele preserva dados transparentes e erros separados. - Use
-Tcomsudo -nem trabalho privilegiado sem supervisão para que a autenticação falhe em vez de esperar. - Use
-tquando uma pessoa digitará uma senha do sudo ou operará uma interface de terminal. - Use
-tta partir de um pipe ou inicializador de agente apenas após revisar a substituição da verificação do TTY local. - Envie tarefas longas a um gerenciador remoto porque um PTY não fornece propriedade duradoura do processo.
Registre a escolha ao lado do comando. Os testes devem verificar tipos de descritor, separação dos fluxos, cancelamento e status, não apenas algumas palavras esperadas. Se uma atualização de dependência passar a exigir terminal, deixe o teste falhar e investigue o novo prompt ou ramo de detecção antes de adicionar -t.
Inclua o modo selecionado nos logs como metadado estruturado, em vez de inferi-lo por saída colorida ou prompt. Quando um incidente atravessa vários hosts, esse único campo permite separar o comportamento do terminal das falhas de autenticação, rede e aplicação antes de executar qualquer coisa novamente.
A política do servidor pode recusar PTYs pela configuração do OpenSSH ou por uma restrição de authorized key. Esse é outro motivo para não torná-los uma dependência acidental. Um comando feito para automação deve continuar utilizável com -T; um comando interativo deve falhar de forma clara quando não puder obter terminal.
Trate qualquer proposta de -tt como mudança de interface. Execute a sonda novamente, redirecione os dois fluxos locais, envie Ctrl-C, force o comando remoto a retornar status diferente de zero e desconecte no meio da execução. Se o resultado não puder ser descrito com precisão, o comando não está pronto para rodar sem supervisão.
FAQ
O que ssh -t realmente faz?
Ele pede ao servidor SSH que aloque um PTY e conecte a sessão remota a ele. Os programas passam a ver descritores de terminal, os fluxos remotos se misturam e as regras de entrada podem gerar sinais.
Qual é a diferença entre ssh -t e ssh -tt?
-t força uma solicitação de PTY quando o cliente local tem terminal. Repetido como -tt, também força a alocação quando a entrada padrão local não é terminal, algo comum em pipelines e agentes.
O SSH aloca um TTY por padrão para comandos remotos?
Normalmente, um comando remoto informado é executado sem PTY. Use -T para deixar essa escolha explícita ou -t quando o comando realmente precisar de um terminal.
Por que sudo falha por SSH sem TTY?
Quando o sudo precisa de senha, normalmente a lê no terminal do usuário. Para automação, use sudo -n e uma regra sudoers limitada, para que o comando falhe em vez de perguntar.
sudo -S é seguro em um script SSH?
sudo -S torna o script responsável pelo transporte da senha e mistura o segredo à entrada padrão. Evite-o para agentes e automação de rotina; use uma autorização que não exiba prompts.
ssh -t carrega .bashrc ou .profile?
Não, a alocação de PTY não escolhe arquivos de inicialização do shell. Solicite explicitamente um shell interativo ou de login se necessário, embora ambiente e caminho explícitos produzam tarefas mais previsíveis.
Por que stdout e stderr se misturam com ssh -t?
O OpenSSH liga os descritores 1 e 2 ao mesmo slave de PTY, então o servidor tem um único fluxo de bytes do terminal. O redirecionamento posterior no cliente não consegue reconstruir os fluxos originais.
Um PTY muda o código de saída do SSH?
O PTY por si só não; o OpenSSH ainda retorna o status do comando remoto. Wrappers, pipelines, sinais e o status especial 255 do cliente podem mudar ou esconder o que o chamador observa.
Por que Ctrl-C se comporta diferente com e sem ssh -t?
O driver de um PTY converte o caractere de interrupção em SIGINT para o grupo em primeiro plano. Sem PTY esse grupo remoto não existe, então o cancelamento depende de sinais SSH e da estrutura dos processos.
Agentes de IA devem usar ssh -tt em comandos que podem perguntar algo?
Não. Um comando para agente deve suprimir prompts e falhar com um status útil; forçar terminal pode transformar erro de configuração em conversa infinita e misturar a saída de diagnóstico.