Tangent uses libvirt to manage a qemu controlled Debian 9 virtual machine and communicates to it through a custom json rpc like protocol.
tangent-server
contains the server that runs as an unprivelaged user on the VM, allowing the bot on the host machine to start processes, read stdin/stdout, and access files safely.
In it's current configuration the VM is on a closed virtual network with only the host machine being routable (192.168.69.1
), iptables are set up so requests from the VM to the host are blocked to prevent it from attempting to connect to SSH or other services it should not have access to.
The only way for information to go in and out of the VM is through connections initiated by the host.
System resources are also heavily limited with 1 thread, 256MB of memory, and a 16GB virtual disk.
If you manage to put the VM in an unusable state for example by killing the server process, Tangent will automatically use virsh to reboot the VM which only takes around 4 seconds.
As a last resort if someone obtains root and bricks the system a qcow2 snapshot can restore the system state to brand new using the qclean
command.
The bot itself is a Dart application and is designed to be as fault tolerant as possible, all buffers that the vm send to are capped, any malformed packets will instantly terminate the connection, and all of the async wrappers for files and processes are destroyed properly when closed.
Currently there are 3 command prefixes for convenience:
.
, α
, @Tangent
(Will have per-server configuration later)
Any of these prefixes can be used to execute commands
purge <n>
- Purges a number of messages (admin only)
qstart
- Start the VM (trusted only)
qrestart
- Restart the VM (trusted only)
qclean
- Revert the VM state to a clean snapshot (trusted only)
upload [file/dir]
- Upload an attachment to the VM at the specified directory or file name, defaults to the home directory
download <file>
- Download a specific file from the VM to Discord
Language commands support discord's code block formatting, for example:
lua ```lua
print("Hello, World!")
```
will interpret the contents of the code block.
You can provide compiler arguments and program arguments by putting text before and after the code block:
.c -ldl ```c
#include <stdio.h>
int main(int argc, char** argv) {
printf("Hello\n");
for (int i = 0; i < argc; i++) {
printf("%i: %s\n", i, argv[i]);
}
}
```
extra arguments
Outputs:
Hello
0: ./tangent
1: extra
2: arguments
The bot is interactive, if you edit your message with a command it will re-run your command and update it's message. When a user enters a new command or edit a previous one it will kill the previous process to prevent anything from lingering.
sh <code>
- Standard /bin/sh
Example
echo Hello from /bin/sh
bash <code>
- Standard /bin/bash
Example
echo Hello from /bin/bash
arm <code>
- ARM Assembly
Example
.globl main
main:
stmfd sp!, {lr}
ldr r0, =hello_text
bl puts
mov r0, #0
ldmfd sp!, {lr}
bx lr
hello_text:
.string "Hello from ARM\n"
x86 <code>
- x86_64 Assembly
Example
.intel_syntax noprefix
.globl main
main:
push rax
mov edi, offset hello_text
xor eax, eax
call puts
xor eax, eax
pop rcx
ret
hello_text:
.string "Hello from x86\n"
c <code>
- GCC 6.3.0
Example
#include <stdio.h>
int main() {
puts("Hello from C\n");
}
cpp <code>
- G++ 6.3.0
Example
#include <iostream>
int main() {
std::cout << "Hello from C++\n";
}
lua <code>
- Lua 5.3 Reference interpreter
lua5.2 <code>
- Lua 5.2 Reference interpreter
lua5.1 <code>
- Lua 5.1 Reference interpreter
luajit <code>
- LuaJIT 2.0
Example
print("Hello from " .. _VERSION)
py <code>
- Python 3
py2 <code>
- Python 2
Example
from platform import python_version
print('Hello from Python {}'.format(python_version()))
js <code>
- Node.JS v4.8.2
Example
console.log("Hello from Node.js " + process.version);
pl <code>
- Perl
Example
print "Hello from Perl\n";
java <code>
- OpenJDK 8
Example
public class Tangent {
public static void main(String[] args) {
System.out.println("Hello from Java");
}
}
lisp <code>
- SBCL
Example
(format t "Hello from Lisp")
bf <code>
- Brainfuck
Example
++++++++++[>+>+++>+++++++>++++++++++<<<<-]>>>++.>+.+++++++..+++.<<++.>>---------.++++++++++++.---.--.<<.>------.>+++++.-----------------.++++++++.+++++.--------.+++++++++++++++.------------------.++++++++.
cs <code>
- .NET Core C#
Example
class Program {
static void Main() {
System.Console.WriteLine("Hello from C#");
}
}
fs <code>
- .NET Core F#
Example
[<EntryPoint>]
let main argv =
printfn "Hello from F#"
0
haskell <code>
- GHC
Example
main = putStrLn "Hello from Haskell"
php <code>
- PHP7
Example
<?php
echo('Hello from PHP');
cobol <code>
- OpenCOBOL
Example
PROGRAM-ID. HELLO.
PROCEDURE DIVISION.
DISPLAY 'Hello from COBOL'.
STOP RUN.
go <code>
- Go 1.7.4
Example
package main
import "fmt"
func main() {
fmt.Println("Hello from Go")
}
ruby <code>
- Ruby 2.3.3
Example
puts 'Hello from Ruby'
apl <code>
- GNU APL
Example
"Hello from APL"
prolog <code>
- SWI Prolog
Example
:- initialization hello, halt.
hello :-
write('Hello from Prolog'), nl.
ocaml <code>
- OCaml 4.02.3
Example
print_string "Hello from OCaml\n";;
sml <code>
- SML-NJ v110.79
Example
print "Hello from SML\n";
crystal <code>
- Crystal 0.28.0
Example
puts "Hello from Crystal"
ada <code>
- GNAT
Example
with Ada.Text_IO; use Ada.Text_IO;
procedure Hello is
begin
Put_Line ("Hello from Ada");
end Hello;
d <code>
- GDC
Example
import std.stdio;
void main() {
writeln("Hello from D");
}
groovy <code>
- Groovy 2.4.8
Example
println "Hello from Groovy"
dart <code>
- Dart 2.3.1
Example
main() {
print("Hello from Dart");
}
erlang <code>
- Erlang
Example
-module(tangent).
-export([main/0]).
main() -> io:fwrite("Hello from Erlang\n").
forth <code>
- GNU FORTH
Example
.( Hello from FORTH) CR
pascal <code>
- Free Pascal Compiler 3.0.0
Example
program Tangent(output);
begin
writeln('Hello from Pascal');
end.
hack <code>
- HipHop VM 4.8.0
Example
echo 'Hello from Hack';
julia <code>
- Julia 0.4.7
Example
println("Hello from Julia")
kotlin <code>
- Kotlin 1.3.31
Example
fun main(args : Array<String>) {
println("Hello from Kotlin")
}
scala <code>
- Scala 2.12.8
Example
object Tangent {
def main(args: Array[String]): Unit = {
println("Hello from Scala")
}
}
swift <code>
- Swift 4.1
Example
import Swift
print("Hello from Swift")```
typescript <code>
- Typescript 2.1.5 on Node.JS v4.8.2
Example
console.log("Hello from TypeScript");
verilog <code>
- Icarus Verilog
Example
module test;
initial begin
$display("Hello from Verilog");
end
endmodule
wasm <code>
- Webassembly WAVM
Example
(module
(import "env" "_fwrite" (func $__fwrite (param i32 i32 i32 i32) (result i32)))
(import "env" "_stdout" (global $stdoutPtr i32))
(import "env" "memory" (memory 1))
(export "main" (func $main))
(data (i32.const 8) "Hello from WASM\n")
(func (export "establishStackSpace") (param i32 i32) (nop))
(func $main (result i32)
(local $stdout i32)
(local.set $stdout (i32.load align=4 (global.get $stdoutPtr)))
(call $__fwrite
(i32.const 8)
(i32.const 1)
(i32.const 16)
(local.get $stdout)
)
(return (i32.const 0))
)
)
scheme <code>
- MIT Scheme 9.1.1
Example
(begin
(display "Hello from Scheme")
(newline))
awk <code>
- GNU Awk 4.1.4
Example
BEGIN { print "Hello from AWK" }
clojure <code>
- Clojure 1.8.0
Example
(println "Hello from Clojure")
tibasic <code>
- Limited TI-BASIC interpreter from patrickfeltes/ti-basic-interpreter
Example
Disp "Hello from TI-BASIC"
batch <code>
- WINE's cmd.exe
Example
@echo off
echo Hello from Batch
racket <code>
- Racket 6.7
Example
#lang racket/base
(print "Hello from Racket")
Rust <code>
- rustc 1.24.1
Example
fn main() {
println!("Hello from Rust");
}
bc <code>
- GNU Basic Calculator
Example
print "Hello from BC\n"
ir <code>
- LLVM IR
Example
@.str = private unnamed_addr constant [20 x i8] c"Hello from LLVM IR\0A\00"
define i32 @main() #0 {
%1 = call i32 @puts(i8* getelementptr inbounds ([20 x i8], [20 x i8]* @.str, i32 0, i32 0))
ret i32 0
}
declare i32 @puts(i8*) #1