Compare commits
No commits in common. "v1.1.0" and "master" have entirely different histories.
114
README.md
@ -1,120 +1,20 @@
|
|||||||
<mark>**WiTTY: Web-based interactive TTY**<mark>
|
# WiTTY: Web-based interactive TTY
|
||||||
|
|
||||||
# Introduction
|
Read document of WiTTY at [**https://syssecfsu.github.io/witty/**](https://syssecfsu.github.io/witty/)
|
||||||
|
|
||||||
This program allows you to use terminal in the browser. Simply run the program and give it the command to execute when users connect via the browser. The design goal of this tool is to help teaching courses that use Unix-like CLI environment. WiTTY has the following features that distinguish itself from other similar tools:
|
WiTTY allows you to use terminal in the browser. Simply run the program and give it the command to execute when users connect via the browser. WiTTY has the following features that distinguish itself from other similar tools:
|
||||||
|
|
||||||
1. WiTTY allows users to **easily record, replay, and share console sessions** with just a few clicks. This make it a breeze to answer course-related questions. Instead of wall of text to describe their questions, students can just send a recorded session.
|
1. WiTTY allows users to **easily record, replay, and share console sessions** with just a few clicks.
|
||||||
|
|
||||||
2. It allows others to **view ongoing interactive sessions**. This is useful for providing live remote help.
|
2. It allows others to **view ongoing interactive sessions**. This is useful for providing live remote help.
|
||||||
|
|
||||||
>A challenge of this use case is that our home networks are almost always behind NAT, making it difficult to run WiTTY as a publicly accessible server. Security is also potentially a concern.
|
|
||||||
|
|
||||||
3. Great attention has been paid to ensure the cleanses of the code. This, hopefully, provides a useful example of **Do as I say and as I do**.
|
3. Great attention has been paid to ensure the cleanses of the code. This, hopefully, provides a useful counter-example of **Do as I say, but not as I do**.
|
||||||
|
|
||||||
# User Interface
|
Here is a screenshot of WiTTY running on Raspberry Pi:
|
||||||
|
|
||||||
You can use WiTTY to run any command line programs, such as ```bash```, ```htop```, ```vi```, ```ssh```. This screenshot shows the main page of WiTTY after login. There are two tabs that list live and recorded sessions, respectively. You can click ```New Session``` to create a new interactive session. Click the <img src="assets/img/view.svg" width="16px"> icon of an interactive session opens a read-only view of that session.
|
|
||||||
|
|
||||||
<img src="extra/main.png" width="800px">
|
|
||||||
|
|
||||||
|
|
||||||
On the interactive terminal window, you can record an ongoing session.
|
|
||||||
|
|
||||||
<img src="extra/interactive.png" width="800px">
|
<img src="extra/interactive.png" width="800px">
|
||||||
|
|
||||||
This screenshot shows three recorded sessions, you can replay <img src="assets/img/play.svg" width="16px">, download <img src="assets/img/download.svg" width="16px">, rename <img src="assets/img/edit.svg" width="16px">, and delete <img src="assets/img/delete.svg" width="16px"> recorded session.
|
You can find more information at [**https://syssecfsu.github.io/witty/**](https://syssecfsu.github.io/witty/)
|
||||||
|
|
||||||
<img src="extra/view.png" width="800px">
|
|
||||||
|
|
||||||
|
|
||||||
Here is a recorded session, where we domonstrate how to use the command line replay utility (in ```cmd/replay```) to replay another recorded session that sshes into a Raspberry Pi running
|
|
||||||
[pi-hole](https://pi-hole.net/). You can fully control the playback using the progress bar.
|
|
||||||
|
|
||||||
>The inception is strong with this one!
|
|
||||||
|
|
||||||
<img src="extra/replay.gif" width="800px">
|
|
||||||
|
|
||||||
|
|
||||||
<!--
|
|
||||||
commands to create high quality gif from mkv/mp4 files
|
|
||||||
ffmpeg -i replay.mkv -vf palettegen palette.png
|
|
||||||
ffmpeg -i replay.mkv -i palette.png -lavfi paletteuse output.gif
|
|
||||||
gifsicle -O3 .\output.gif -o replay.gif
|
|
||||||
-->
|
|
||||||
|
|
||||||
|
|
||||||
This program is written in the [go programming language](https://go.dev/), using the
|
|
||||||
[Gin web framework](https://github.com/gin-gonic/gin), [gorilla/websocket](https://github.com/gorilla/websocket), [pty](https://github.com/creack/pty), and the wonderful [xterm.js](https://xtermjs.org/)!
|
|
||||||
The workflow is simple, the client will initiate a terminal
|
|
||||||
window (xterm.js) and create a websocket with the server, which relays the data between pty and xterm. You can customize the look and feel of the HTML pages by editing files under the ```assets``` directory.
|
|
||||||
|
|
||||||
The program has been tested on Linux, WSL2, Raspberry Pi 3B (Debian), and MacOSX using Google Chrome, Firefox, and Safari.
|
|
||||||
|
|
||||||
Most icons were provided by [fontawesome](https://fontawesome.com/) under this [license](https://fontawesome.com/license).
|
|
||||||
|
|
||||||
# Installation
|
|
||||||
|
|
||||||
1. Install the [go](https://go.dev/) compiler. __Make sure you have go 1.17 or higher.__
|
|
||||||
2. Download the release and unzip it, or clone the repo
|
|
||||||
|
|
||||||
```git clone https://github.com/syssecfsu/witty.git```
|
|
||||||
|
|
||||||
3. WiTTY uses TLS to protect its traffic. You can request a free [Let's Encrypt](https://letsencrypt.org/) cert or use a self-signed cert. Here is how to create a self-signed cert in the ```tls``` sub-directory:
|
|
||||||
|
|
||||||
\# Generate a private key for a curve
|
|
||||||
|
|
||||||
```openssl ecparam -name prime256v1 -genkey -noout -out private-key.pem```
|
|
||||||
|
|
||||||
\# Create a self-signed certificate
|
|
||||||
|
|
||||||
```openssl req -new -x509 -key private-key.pem -out cert.pem -days 360```
|
|
||||||
|
|
||||||
4. Return to the root directory of the source code and build the program
|
|
||||||
|
|
||||||
```go build .```
|
|
||||||
|
|
||||||
5. Add a user to the user accounts, follow the instructions on screen to provide the password
|
|
||||||
|
|
||||||
```./witty adduser <username>```
|
|
||||||
|
|
||||||
6. Start the server and give it the command to run. By default, the server listens on 8080 (you can override it with the ```-p/-port``` option):
|
|
||||||
|
|
||||||
```./witty run htop``` or
|
|
||||||
|
|
||||||
```./witty run -p 9000 ssh <ssh_server_ip> -l <user_name>```
|
|
||||||
|
|
||||||
If so desired, you can disable user authenticate with ```-n/-naked```, (not recommended) for example:
|
|
||||||
|
|
||||||
```./witty run -naked htop```
|
|
||||||
|
|
||||||
7. Connect to the server with your browser at port 8080 or the one specified in step 6, for example
|
|
||||||
|
|
||||||
```https://<witty_server_ip>:8080```
|
|
||||||
|
|
||||||
# User Authentication
|
|
||||||
|
|
||||||
WiTTY uses username/password based authentication. The user database is stored in ```user.db```. The passwords are salted with 64 bytes of random characters and then hashed using SHA256. WiTTY has three sub-commands to manage ```user.db```.
|
|
||||||
|
|
||||||
- ```witty adduser <username>```
|
|
||||||
- ```witty deluser <username>```
|
|
||||||
- ```witty listusers```
|
|
||||||
|
|
||||||
They are pretty self-explanatory. Just follow the instructions on screen. Note that passwords must be 12 bytes or longer.
|
|
||||||
|
|
||||||
# Recorded Sessions
|
|
||||||
|
|
||||||
WiTTY provides two sub-commands to merge and replay recorded sessions.
|
|
||||||
|
|
||||||
- ```witty replay -w <wait_time> <recorded_session>```
|
|
||||||
- ```witty merge -o <output_file> <record1> <record2> ...```
|
|
||||||
|
|
||||||
Recorded sessions often have long delay between outputs. You can set wait_time to limit the maximum wait time between outputs, to speed up the replay.
|
|
||||||
|
|
||||||
You can also use ```witty merge``` to merge two or more recorded sessions, as shown below.
|
|
||||||
|
|
||||||
<img src="extra/merge.png" width="640px">
|
|
||||||
|
|
||||||
The intended use case is to record a separate session for each individual task, rename and merge them into a final session for submission. The following screenshot shows how to rename a recorded session, and combine them into the final session (you can also rename a recorded session using commands in a shell. All the recorded sessions are located under the ```records``` directory).
|
|
||||||
|
|
||||||
<img src="extra/rename.png" width="800px">
|
|
||||||
|
Before Width: | Height: | Size: 15 KiB |
@ -1,350 +1 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
<svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="terminal" class="svg-inline--fa fa-terminal fa-w-20" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><path fill="currentColor" d="M257.981 272.971L63.638 467.314c-9.373 9.373-24.569 9.373-33.941 0L7.029 444.647c-9.357-9.357-9.375-24.522-.04-33.901L161.011 256 6.99 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L257.981 239.03c9.373 9.372 9.373 24.568 0 33.941zM640 456v-32c0-13.255-10.745-24-24-24H312c-13.255 0-24 10.745-24 24v32c0 13.255 10.745 24 24 24h304c13.255 0 24-10.745 24-24z"></path></svg>
|
||||||
<svg
|
|
||||||
width="48"
|
|
||||||
height="36"
|
|
||||||
viewBox="0 0 48 36"
|
|
||||||
fill="none"
|
|
||||||
version="1.1"
|
|
||||||
id="svg136"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg">
|
|
||||||
<path
|
|
||||||
d="M 0,7 H 16 V 0 H 2 C 0.9,0 0,0.9 0,2 Z"
|
|
||||||
fill="#cccccc"
|
|
||||||
id="path2" />
|
|
||||||
<path
|
|
||||||
d="M 32,0 H 16 v 7 h 16 z"
|
|
||||||
fill="#999999"
|
|
||||||
id="path4" />
|
|
||||||
<path
|
|
||||||
d="M 48,7 H 32 V 0 h 14 c 1.1,0 2,0.9 2,2 z"
|
|
||||||
fill="#666666"
|
|
||||||
id="path6" />
|
|
||||||
<path
|
|
||||||
d="M 46,36 H 2 C 0.9,36 0,35.1 0,34 V 6 h 48 v 28 c 0,1.1 -0.9,2 -2,2 z"
|
|
||||||
fill="url(#paint0_linear)"
|
|
||||||
id="path8"
|
|
||||||
style="fill:url(#paint0_linear)" />
|
|
||||||
<g
|
|
||||||
filter="url(#filter0_dd)"
|
|
||||||
id="g23"
|
|
||||||
transform="translate(0,-6)">
|
|
||||||
<path
|
|
||||||
d="m 15.2,24.3 -8.80001,8.8 c -0.5,0.5 -0.5,1.2 0,1.6 l 1.8,1.8 C 8.69999,37 9.4,37 9.8,36.5 l 8.8,-8.8 c 0.5,-0.5 0.5,-1.2 0,-1.6 l -1.8,-1.8 c -0.4,-0.4 -1.2,-0.4 -1.6,0 z"
|
|
||||||
fill="url(#paint1_linear)"
|
|
||||||
id="path10"
|
|
||||||
style="fill:url(#paint1_linear)" />
|
|
||||||
<mask
|
|
||||||
id="mask0"
|
|
||||||
mask-type="alpha"
|
|
||||||
maskUnits="userSpaceOnUse"
|
|
||||||
x="6"
|
|
||||||
y="24"
|
|
||||||
width="13"
|
|
||||||
height="13">
|
|
||||||
<path
|
|
||||||
d="m 15.2,24.3 -8.80001,8.8 c -0.5,0.5 -0.5,1.2 0,1.6 l 1.8,1.8 C 8.69999,37 9.4,37 9.8,36.5 l 8.8,-8.8 c 0.5,-0.5 0.5,-1.2 0,-1.6 l -1.8,-1.8 c -0.4,-0.4 -1.2,-0.4 -1.6,0 z"
|
|
||||||
fill="url(#paint2_linear)"
|
|
||||||
id="path12" />
|
|
||||||
</mask>
|
|
||||||
<g
|
|
||||||
mask="url(#mask0)"
|
|
||||||
id="g19">
|
|
||||||
<g
|
|
||||||
filter="url(#filter1_dd)"
|
|
||||||
id="g17">
|
|
||||||
<path
|
|
||||||
d="m 9.8,17.3 8.8,8.8 c 0.5,0.5 0.5,1.2 0,1.6 l -1.8,1.8 c -0.5,0.5 -1.2,0.5 -1.6,0 L 6.39999,20.7 c -0.5,-0.5 -0.5,-1.2 0,-1.6 l 1.8,-1.8 C 8.59999,16.9 9.4,16.9 9.8,17.3 Z"
|
|
||||||
fill="url(#paint3_linear)"
|
|
||||||
id="path15"
|
|
||||||
style="fill:url(#paint3_linear)" />
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
<path
|
|
||||||
d="m 9.8,17.3 8.8,8.8 c 0.5,0.5 0.5,1.2 0,1.6 l -1.8,1.8 c -0.5,0.5 -1.2,0.5 -1.6,0 L 6.39999,20.7 c -0.5,-0.5 -0.5,-1.2 0,-1.6 l 1.8,-1.8 C 8.59999,16.9 9.4,16.9 9.8,17.3 Z"
|
|
||||||
fill="url(#paint4_linear)"
|
|
||||||
id="path21"
|
|
||||||
style="fill:url(#paint4_linear)" />
|
|
||||||
</g>
|
|
||||||
<g
|
|
||||||
filter="url(#filter2_dd)"
|
|
||||||
id="g27"
|
|
||||||
transform="translate(0,-6)">
|
|
||||||
<path
|
|
||||||
d="M 40,32 H 24 c -0.6,0 -1,0.4 -1,1 v 3 c 0,0.6 0.4,1 1,1 h 16 c 0.6,0 1,-0.4 1,-1 v -3 c 0,-0.6 -0.4,-1 -1,-1 z"
|
|
||||||
fill="url(#paint5_linear)"
|
|
||||||
id="path25"
|
|
||||||
style="fill:url(#paint5_linear)" />
|
|
||||||
</g>
|
|
||||||
<defs
|
|
||||||
id="defs134">
|
|
||||||
<filter
|
|
||||||
id="filter0_dd"
|
|
||||||
x="3.0249901"
|
|
||||||
y="15"
|
|
||||||
width="18.950001"
|
|
||||||
height="25.875"
|
|
||||||
filterUnits="userSpaceOnUse"
|
|
||||||
color-interpolation-filters="sRGB">
|
|
||||||
<feFlood
|
|
||||||
flood-opacity="0"
|
|
||||||
result="BackgroundImageFix"
|
|
||||||
id="feFlood29" />
|
|
||||||
<feColorMatrix
|
|
||||||
in="SourceAlpha"
|
|
||||||
type="matrix"
|
|
||||||
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"
|
|
||||||
id="feColorMatrix31" />
|
|
||||||
<feOffset
|
|
||||||
dy="0.5"
|
|
||||||
id="feOffset33" />
|
|
||||||
<feGaussianBlur
|
|
||||||
stdDeviation="0.5"
|
|
||||||
id="feGaussianBlur35" />
|
|
||||||
<feColorMatrix
|
|
||||||
type="matrix"
|
|
||||||
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.1 0"
|
|
||||||
id="feColorMatrix37" />
|
|
||||||
<feBlend
|
|
||||||
mode="normal"
|
|
||||||
in2="BackgroundImageFix"
|
|
||||||
result="effect1_dropShadow"
|
|
||||||
id="feBlend39" />
|
|
||||||
<feColorMatrix
|
|
||||||
in="SourceAlpha"
|
|
||||||
type="matrix"
|
|
||||||
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"
|
|
||||||
id="feColorMatrix41" />
|
|
||||||
<feOffset
|
|
||||||
dy="1"
|
|
||||||
id="feOffset43" />
|
|
||||||
<feGaussianBlur
|
|
||||||
stdDeviation="1.5"
|
|
||||||
id="feGaussianBlur45" />
|
|
||||||
<feColorMatrix
|
|
||||||
type="matrix"
|
|
||||||
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.2 0"
|
|
||||||
id="feColorMatrix47" />
|
|
||||||
<feBlend
|
|
||||||
mode="normal"
|
|
||||||
in2="effect1_dropShadow"
|
|
||||||
result="effect2_dropShadow"
|
|
||||||
id="feBlend49" />
|
|
||||||
<feBlend
|
|
||||||
mode="normal"
|
|
||||||
in="SourceGraphic"
|
|
||||||
in2="effect2_dropShadow"
|
|
||||||
result="shape"
|
|
||||||
id="feBlend51" />
|
|
||||||
</filter>
|
|
||||||
<filter
|
|
||||||
id="filter1_dd"
|
|
||||||
x="3.0249901"
|
|
||||||
y="15"
|
|
||||||
width="18.950001"
|
|
||||||
height="18.875"
|
|
||||||
filterUnits="userSpaceOnUse"
|
|
||||||
color-interpolation-filters="sRGB">
|
|
||||||
<feFlood
|
|
||||||
flood-opacity="0"
|
|
||||||
result="BackgroundImageFix"
|
|
||||||
id="feFlood54" />
|
|
||||||
<feColorMatrix
|
|
||||||
in="SourceAlpha"
|
|
||||||
type="matrix"
|
|
||||||
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"
|
|
||||||
id="feColorMatrix56" />
|
|
||||||
<feOffset
|
|
||||||
dy="0.5"
|
|
||||||
id="feOffset58" />
|
|
||||||
<feGaussianBlur
|
|
||||||
stdDeviation="0.5"
|
|
||||||
id="feGaussianBlur60" />
|
|
||||||
<feColorMatrix
|
|
||||||
type="matrix"
|
|
||||||
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.1 0"
|
|
||||||
id="feColorMatrix62" />
|
|
||||||
<feBlend
|
|
||||||
mode="normal"
|
|
||||||
in2="BackgroundImageFix"
|
|
||||||
result="effect1_dropShadow"
|
|
||||||
id="feBlend64" />
|
|
||||||
<feColorMatrix
|
|
||||||
in="SourceAlpha"
|
|
||||||
type="matrix"
|
|
||||||
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"
|
|
||||||
id="feColorMatrix66" />
|
|
||||||
<feOffset
|
|
||||||
dy="1"
|
|
||||||
id="feOffset68" />
|
|
||||||
<feGaussianBlur
|
|
||||||
stdDeviation="1.5"
|
|
||||||
id="feGaussianBlur70" />
|
|
||||||
<feColorMatrix
|
|
||||||
type="matrix"
|
|
||||||
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.2 0"
|
|
||||||
id="feColorMatrix72" />
|
|
||||||
<feBlend
|
|
||||||
mode="normal"
|
|
||||||
in2="effect1_dropShadow"
|
|
||||||
result="effect2_dropShadow"
|
|
||||||
id="feBlend74" />
|
|
||||||
<feBlend
|
|
||||||
mode="normal"
|
|
||||||
in="SourceGraphic"
|
|
||||||
in2="effect2_dropShadow"
|
|
||||||
result="shape"
|
|
||||||
id="feBlend76" />
|
|
||||||
</filter>
|
|
||||||
<filter
|
|
||||||
id="filter2_dd"
|
|
||||||
x="20"
|
|
||||||
y="30"
|
|
||||||
width="24"
|
|
||||||
height="11"
|
|
||||||
filterUnits="userSpaceOnUse"
|
|
||||||
color-interpolation-filters="sRGB">
|
|
||||||
<feFlood
|
|
||||||
flood-opacity="0"
|
|
||||||
result="BackgroundImageFix"
|
|
||||||
id="feFlood79" />
|
|
||||||
<feColorMatrix
|
|
||||||
in="SourceAlpha"
|
|
||||||
type="matrix"
|
|
||||||
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"
|
|
||||||
id="feColorMatrix81" />
|
|
||||||
<feOffset
|
|
||||||
dy="0.5"
|
|
||||||
id="feOffset83" />
|
|
||||||
<feGaussianBlur
|
|
||||||
stdDeviation="0.5"
|
|
||||||
id="feGaussianBlur85" />
|
|
||||||
<feColorMatrix
|
|
||||||
type="matrix"
|
|
||||||
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.1 0"
|
|
||||||
id="feColorMatrix87" />
|
|
||||||
<feBlend
|
|
||||||
mode="normal"
|
|
||||||
in2="BackgroundImageFix"
|
|
||||||
result="effect1_dropShadow"
|
|
||||||
id="feBlend89" />
|
|
||||||
<feColorMatrix
|
|
||||||
in="SourceAlpha"
|
|
||||||
type="matrix"
|
|
||||||
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"
|
|
||||||
id="feColorMatrix91" />
|
|
||||||
<feOffset
|
|
||||||
dy="1"
|
|
||||||
id="feOffset93" />
|
|
||||||
<feGaussianBlur
|
|
||||||
stdDeviation="1.5"
|
|
||||||
id="feGaussianBlur95" />
|
|
||||||
<feColorMatrix
|
|
||||||
type="matrix"
|
|
||||||
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.2 0"
|
|
||||||
id="feColorMatrix97" />
|
|
||||||
<feBlend
|
|
||||||
mode="normal"
|
|
||||||
in2="effect1_dropShadow"
|
|
||||||
result="effect2_dropShadow"
|
|
||||||
id="feBlend99" />
|
|
||||||
<feBlend
|
|
||||||
mode="normal"
|
|
||||||
in="SourceGraphic"
|
|
||||||
in2="effect2_dropShadow"
|
|
||||||
result="shape"
|
|
||||||
id="feBlend101" />
|
|
||||||
</filter>
|
|
||||||
<linearGradient
|
|
||||||
id="paint0_linear"
|
|
||||||
x1="36.446201"
|
|
||||||
y1="47.825699"
|
|
||||||
x2="11.8217"
|
|
||||||
y2="5.1747999"
|
|
||||||
gradientUnits="userSpaceOnUse"
|
|
||||||
gradientTransform="translate(0,-6)">
|
|
||||||
<stop
|
|
||||||
stop-color="#333333"
|
|
||||||
id="stop104" />
|
|
||||||
<stop
|
|
||||||
offset="1"
|
|
||||||
stop-color="#4D4D4D"
|
|
||||||
id="stop106" />
|
|
||||||
</linearGradient>
|
|
||||||
<linearGradient
|
|
||||||
id="paint1_linear"
|
|
||||||
x1="14.5276"
|
|
||||||
y1="33.995899"
|
|
||||||
x2="10.4841"
|
|
||||||
y2="26.992399"
|
|
||||||
gradientUnits="userSpaceOnUse">
|
|
||||||
<stop
|
|
||||||
stop-color="#999999"
|
|
||||||
id="stop109" />
|
|
||||||
<stop
|
|
||||||
offset="1"
|
|
||||||
stop-color="#B3B3B3"
|
|
||||||
id="stop111" />
|
|
||||||
</linearGradient>
|
|
||||||
<linearGradient
|
|
||||||
id="paint2_linear"
|
|
||||||
x1="14.5276"
|
|
||||||
y1="33.995899"
|
|
||||||
x2="10.4841"
|
|
||||||
y2="26.992399"
|
|
||||||
gradientUnits="userSpaceOnUse">
|
|
||||||
<stop
|
|
||||||
stop-color="#999999"
|
|
||||||
id="stop114" />
|
|
||||||
<stop
|
|
||||||
offset="1"
|
|
||||||
stop-color="#B3B3B3"
|
|
||||||
id="stop116" />
|
|
||||||
</linearGradient>
|
|
||||||
<linearGradient
|
|
||||||
id="paint3_linear"
|
|
||||||
x1="16.2747"
|
|
||||||
y1="30.0336"
|
|
||||||
x2="8.73699"
|
|
||||||
y2="16.9781"
|
|
||||||
gradientUnits="userSpaceOnUse">
|
|
||||||
<stop
|
|
||||||
stop-color="#CCCCCC"
|
|
||||||
id="stop119" />
|
|
||||||
<stop
|
|
||||||
offset="1"
|
|
||||||
stop-color="#E6E6E6"
|
|
||||||
id="stop121" />
|
|
||||||
</linearGradient>
|
|
||||||
<linearGradient
|
|
||||||
id="paint4_linear"
|
|
||||||
x1="16.2747"
|
|
||||||
y1="30.0336"
|
|
||||||
x2="8.73699"
|
|
||||||
y2="16.9781"
|
|
||||||
gradientUnits="userSpaceOnUse">
|
|
||||||
<stop
|
|
||||||
stop-color="#CCCCCC"
|
|
||||||
id="stop124" />
|
|
||||||
<stop
|
|
||||||
offset="1"
|
|
||||||
stop-color="#E6E6E6"
|
|
||||||
id="stop126" />
|
|
||||||
</linearGradient>
|
|
||||||
<linearGradient
|
|
||||||
id="paint5_linear"
|
|
||||||
x1="35.149601"
|
|
||||||
y1="39.955299"
|
|
||||||
x2="28.850401"
|
|
||||||
y2="29.044701"
|
|
||||||
gradientUnits="userSpaceOnUse">
|
|
||||||
<stop
|
|
||||||
stop-color="#CCCCCC"
|
|
||||||
id="stop129" />
|
|
||||||
<stop
|
|
||||||
offset="1"
|
|
||||||
stop-color="#E6E6E6"
|
|
||||||
id="stop131" />
|
|
||||||
</linearGradient>
|
|
||||||
</defs>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 9.3 KiB After Width: | Height: | Size: 637 B |
59
assets/img/logo_light.svg
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
inkscape:version="1.0 (4035a4f, 2020-05-01)"
|
||||||
|
sodipodi:docname="logo-light.svg"
|
||||||
|
id="svg835"
|
||||||
|
version="1.1"
|
||||||
|
viewBox="0 0 640 512"
|
||||||
|
role="img"
|
||||||
|
class="svg-inline--fa fa-laptop-code fa-w-20"
|
||||||
|
data-icon="laptop-code"
|
||||||
|
data-prefix="fas"
|
||||||
|
focusable="false"
|
||||||
|
aria-hidden="true">
|
||||||
|
<metadata
|
||||||
|
id="metadata841">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<defs
|
||||||
|
id="defs839" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
inkscape:current-layer="svg835"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:window-y="25"
|
||||||
|
inkscape:window-x="56"
|
||||||
|
inkscape:cy="256"
|
||||||
|
inkscape:cx="320"
|
||||||
|
inkscape:zoom="1.1125"
|
||||||
|
showgrid="false"
|
||||||
|
id="namedview837"
|
||||||
|
inkscape:window-height="997"
|
||||||
|
inkscape:window-width="1624"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
guidetolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
objecttolerance="10"
|
||||||
|
borderopacity="1"
|
||||||
|
bordercolor="#666666"
|
||||||
|
pagecolor="#ffffff" />
|
||||||
|
<path
|
||||||
|
style="fill:#f0f0f0;fill-opacity:0.95999998"
|
||||||
|
id="path833"
|
||||||
|
d="M255.03 261.65c6.25 6.25 16.38 6.25 22.63 0l11.31-11.31c6.25-6.25 6.25-16.38 0-22.63L253.25 192l35.71-35.72c6.25-6.25 6.25-16.38 0-22.63l-11.31-11.31c-6.25-6.25-16.38-6.25-22.63 0l-58.34 58.34c-6.25 6.25-6.25 16.38 0 22.63l58.35 58.34zm96.01-11.3l11.31 11.31c6.25 6.25 16.38 6.25 22.63 0l58.34-58.34c6.25-6.25 6.25-16.38 0-22.63l-58.34-58.34c-6.25-6.25-16.38-6.25-22.63 0l-11.31 11.31c-6.25 6.25-6.25 16.38 0 22.63L386.75 192l-35.71 35.72c-6.25 6.25-6.25 16.38 0 22.63zM624 416H381.54c-.74 19.81-14.71 32-32.74 32H288c-18.69 0-33.02-17.47-32.77-32H16c-8.8 0-16 7.2-16 16v16c0 35.2 28.8 64 64 64h512c35.2 0 64-28.8 64-64v-16c0-8.8-7.2-16-16-16zM576 48c0-26.4-21.6-48-48-48H112C85.6 0 64 21.6 64 48v336h512V48zm-64 272H128V64h384v256z"
|
||||||
|
fill="currentColor" />
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.4 KiB |
1
assets/img/term.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="window-restore" class="svg-inline--fa fa-window-restore fa-w-16" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M512 48v288c0 26.5-21.5 48-48 48h-48V176c0-44.1-35.9-80-80-80H128V48c0-26.5 21.5-48 48-48h288c26.5 0 48 21.5 48 48zM384 176v288c0 26.5-21.5 48-48 48H48c-26.5 0-48-21.5-48-48V176c0-26.5 21.5-48 48-48h288c26.5 0 48 21.5 48 48zm-68 28c0-6.6-5.4-12-12-12H76c-6.6 0-12 5.4-12 12v52h252v-52z"></path></svg>
|
After Width: | Height: | Size: 533 B |
@ -88,7 +88,7 @@ function base64ToUint8array(base64) {
|
|||||||
// shift: callback whether we should change position
|
// shift: callback whether we should change position
|
||||||
// end: callback when playback is finished
|
// end: callback when playback is finished
|
||||||
|
|
||||||
async function replay_session(term, records, total_dur, start, paused, prog, end) {
|
async function replay_session(term, records, max_wait, total_dur, start, paused, prog, end) {
|
||||||
var cur = 0
|
var cur = 0
|
||||||
|
|
||||||
start = parseInt(total_dur * start / 100)
|
start = parseInt(total_dur * start / 100)
|
||||||
@ -98,7 +98,7 @@ async function replay_session(term, records, total_dur, start, paused, prog, end
|
|||||||
// we will blast through the beginning of the session
|
// we will blast through the beginning of the session
|
||||||
if (cur >= start) {
|
if (cur >= start) {
|
||||||
// we are cheating a little bit here, we do not want to wait for too long
|
// we are cheating a little bit here, we do not want to wait for too long
|
||||||
exit = await sleep(Math.min(item.Duration, 1000), paused)
|
exit = await sleep(Math.min(item.Duration, max_wait), paused)
|
||||||
|
|
||||||
if (exit) {
|
if (exit) {
|
||||||
return
|
return
|
||||||
|
@ -6,9 +6,7 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||||
<meta name="description" content="">
|
<meta name="description" content="">
|
||||||
<meta name="author" content="">
|
<meta name="author" content="">
|
||||||
|
<link rel="icon" type="image/x-icon" href="/assets/img/logo.svg ">
|
||||||
<!-- automatically refresh the page every 30 seconds -->
|
|
||||||
<!-- <meta http-equiv="refresh" content="30"> -->
|
|
||||||
|
|
||||||
<title>Web Terminal</title>
|
<title>Web Terminal</title>
|
||||||
|
|
||||||
@ -20,11 +18,10 @@
|
|||||||
|
|
||||||
<body>
|
<body>
|
||||||
<header>
|
<header>
|
||||||
<nav class="navbar navbar-dark bg-dark shadow-sm navbar-xs">
|
<nav class="navbar navbar-dark shadow-sm navbar-xs" style="background-color: #002f55;">
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
<a class="navbar-brand mx-auto" href="https://github.com/syssecfsu/witty" target="_blank">
|
<a class="navbar-brand mx-auto" href="https://github.com/syssecfsu/witty" target="_blank">
|
||||||
<img src="/assets/img/logo.svg" style="margin-right: 0.5rem;" height="32"
|
<img src="/assets/img/logo_light.svg" style="margin-right: 0.5rem;" height="24">
|
||||||
class="d-inline-block align-text-top">
|
|
||||||
WiTTY: Web-based interactive TTY
|
WiTTY: Web-based interactive TTY
|
||||||
</a>
|
</a>
|
||||||
<div class="btn-toolbar float-end" role="toolbar" aria-label="top buttons">
|
<div class="btn-toolbar float-end" role="toolbar" aria-label="top buttons">
|
||||||
@ -61,7 +58,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<main>
|
<main>
|
||||||
<div class="container-fluid" style="margin-top:1em;">
|
<div class="container-fluid" style="margin-top:2em;">
|
||||||
<ul class="nav nav-tabs" id="js_sucks" role="tablist">
|
<ul class="nav nav-tabs" id="js_sucks" role="tablist">
|
||||||
<li class="nav-item" role="presentation">
|
<li class="nav-item" role="presentation">
|
||||||
<button class="nav-link bg-light active" id="interactive-tab" data-bs-toggle="tab"
|
<button class="nav-link bg-light active" id="interactive-tab" data-bs-toggle="tab"
|
||||||
@ -76,7 +73,7 @@
|
|||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<div class="tab-content bg-light border border-info rounded-bottom" id="nav-tabContent">
|
<div class="tab-content border border-info rounded-bottom" id="nav-tabContent">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
@ -160,7 +157,6 @@
|
|||||||
refresh(true)
|
refresh(true)
|
||||||
}, 20);
|
}, 20);
|
||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||||
<meta name="description" content="">
|
<meta name="description" content="">
|
||||||
<meta name="author" content="">
|
<meta name="author" content="">
|
||||||
|
<link rel="icon" type="image/x-icon" href="/assets/img/logo.svg">
|
||||||
|
|
||||||
<title>WiTTY Login</title>
|
<title>WiTTY Login</title>
|
||||||
<script src="/assets/external/bootstrap.min.js"></script>
|
<script src="/assets/external/bootstrap.min.js"></script>
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
|
<link rel="icon" type="image/x-icon" href="/assets/img/logo.svg">
|
||||||
|
|
||||||
<script src="/assets/external/xterm.js"></script>
|
<script src="/assets/external/xterm.js"></script>
|
||||||
<script src="/assets/external/xterm-addon-attach.js"></script>
|
<script src="/assets/external/xterm-addon-attach.js"></script>
|
||||||
@ -68,7 +69,7 @@
|
|||||||
if (icon.src.includes("play")) {
|
if (icon.src.includes("play")) {
|
||||||
icon.src = "/assets/img/pause.svg"
|
icon.src = "/assets/img/pause.svg"
|
||||||
pause = false
|
pause = false
|
||||||
replay_session(term, records, total_dur, rc.value,
|
replay_session(term, records, {{.max_wait}}, total_dur, rc.value,
|
||||||
function () {
|
function () {
|
||||||
return pause
|
return pause
|
||||||
},
|
},
|
||||||
|
@ -24,7 +24,7 @@
|
|||||||
|
|
||||||
<!-- repeat this for each recorded session -->
|
<!-- repeat this for each recorded session -->
|
||||||
{{range .records}}
|
{{range .records}}
|
||||||
<div class="card shadow-sm border-info bg-light mb-3" style="width: 16rem; margin:1em;">
|
<div class="card shadow-sm border-info mb-3" style="width: 16rem; margin:1em;">
|
||||||
<div class="card-body d-flex flex-column">
|
<div class="card-body d-flex flex-column">
|
||||||
<h5 class="card-title">Recorded session</h5>
|
<h5 class="card-title">Recorded session</h5>
|
||||||
<p class="card-text">File name: <u>{{.Fname}}</u>, file size: <em>{{.Fsize}}KB</em>,
|
<p class="card-text">File name: <u>{{.Fname}}</u>, file size: <em>{{.Fsize}}KB</em>,
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
|
<link rel="icon" type="image/x-icon" href="/assets/img/logo.svg">
|
||||||
|
|
||||||
<script src="/assets/external/xterm.js"></script>
|
<script src="/assets/external/xterm.js"></script>
|
||||||
<script src="/assets/external/xterm-addon-attach.js"></script>
|
<script src="/assets/external/xterm-addon-attach.js"></script>
|
||||||
|
12
build.sh
Executable file
@ -0,0 +1,12 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
go clean .
|
||||||
|
go build .
|
||||||
|
|
||||||
|
DST="./release"
|
||||||
|
rm -rf $DST
|
||||||
|
mkdir $DST
|
||||||
|
mkdir $DST/tls
|
||||||
|
mkdir $DST/records
|
||||||
|
cp tls/README.md $DST/tls/README.md
|
||||||
|
cp witty LICENSE $DST
|
||||||
|
echo "[]" > $DST/user.db
|
@ -1,15 +1,17 @@
|
|||||||
package term_conn
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/syssecfsu/witty/term_conn"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Merge(fnames []string, output string) {
|
func Merge(fnames []string, output string) {
|
||||||
var all_recrods []writeRecord
|
var all_recrods []term_conn.WriteRecord
|
||||||
var records []writeRecord
|
var records []term_conn.WriteRecord
|
||||||
|
|
||||||
for _, fname := range fnames {
|
for _, fname := range fnames {
|
||||||
file, err := os.ReadFile(fname)
|
file, err := os.ReadFile(fname)
|
@ -1,4 +1,4 @@
|
|||||||
package term_conn
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
@ -7,14 +7,10 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/syssecfsu/witty/term_conn"
|
||||||
"golang.org/x/term"
|
"golang.org/x/term"
|
||||||
)
|
)
|
||||||
|
|
||||||
type writeRecord struct {
|
|
||||||
Dur time.Duration `json:"Duration"`
|
|
||||||
Data []byte `json:"Data"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func Replay(fname string, wait uint) {
|
func Replay(fname string, wait uint) {
|
||||||
fp, err := os.Open(fname)
|
fp, err := os.Open(fname)
|
||||||
|
|
||||||
@ -52,7 +48,7 @@ func Replay(fname string, wait uint) {
|
|||||||
|
|
||||||
decoder.Token()
|
decoder.Token()
|
||||||
for decoder.More() {
|
for decoder.More() {
|
||||||
var record writeRecord
|
var record term_conn.WriteRecord
|
||||||
|
|
||||||
if err := decoder.Decode(&record); err != nil {
|
if err := decoder.Decode(&record); err != nil {
|
||||||
log.Println("Failed to decode record", err)
|
log.Println("Failed to decode record", err)
|
@ -1,84 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"flag"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"golang.org/x/term"
|
|
||||||
)
|
|
||||||
|
|
||||||
type writeRecord struct {
|
|
||||||
Dur time.Duration `json:"Duration"`
|
|
||||||
Data []byte `json:"Data"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
var wait uint
|
|
||||||
|
|
||||||
flag.UintVar(&wait, "w", 2000, "Max wait time between outputs")
|
|
||||||
flag.UintVar(&wait, "wait", 2000, "Max wait time between outputs")
|
|
||||||
flag.Parse()
|
|
||||||
|
|
||||||
left := flag.Args()
|
|
||||||
|
|
||||||
if len(left) != 1 {
|
|
||||||
fmt.Println("Usage: replay -w/wait time <recordfile>")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
fp, err := os.Open(left[0])
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalln("Failed to open record file", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
screen := struct {
|
|
||||||
io.Reader
|
|
||||||
io.Writer
|
|
||||||
}{os.Stdin, os.Stdout}
|
|
||||||
|
|
||||||
t := term.NewTerminal(screen, "$")
|
|
||||||
|
|
||||||
if t == nil {
|
|
||||||
log.Fatalln("Failed to create terminal")
|
|
||||||
}
|
|
||||||
|
|
||||||
w, h, _ := term.GetSize(int(os.Stdout.Fd()))
|
|
||||||
|
|
||||||
if (w != 120) || (h != 36) {
|
|
||||||
log.Println("Set terminal window to 120x36 before continue")
|
|
||||||
}
|
|
||||||
|
|
||||||
decoder := json.NewDecoder(fp)
|
|
||||||
|
|
||||||
if decoder == nil {
|
|
||||||
log.Fatalln("Failed to create JSON decoder")
|
|
||||||
}
|
|
||||||
|
|
||||||
// To work with javascript decoder, we organize the file as
|
|
||||||
// an array of writeRecord. golang decode instead decode
|
|
||||||
// as individual record. Call decoder.Token to skip opening [
|
|
||||||
decoder.Token()
|
|
||||||
for decoder.More() {
|
|
||||||
var record writeRecord
|
|
||||||
|
|
||||||
if err := decoder.Decode(&record); err != nil {
|
|
||||||
log.Println("Failed to decode record", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if record.Dur > time.Duration(wait)*time.Millisecond {
|
|
||||||
record.Dur = time.Duration(wait) * time.Millisecond
|
|
||||||
}
|
|
||||||
|
|
||||||
time.Sleep(record.Dur)
|
|
||||||
t.Write(record.Data)
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Write([]byte("\n\n---end of replay---\n\n"))
|
|
||||||
}
|
|
@ -1,4 +0,0 @@
|
|||||||
echo "Try to resize shell with shell command"
|
|
||||||
printf '\e[8;36;120t'
|
|
||||||
clear
|
|
||||||
$(dirname $0)/replay "$@"
|
|
@ -1,4 +1,4 @@
|
|||||||
package web
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
30
docs/404.html
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
<!doctype html><html lang=en>
|
||||||
|
<head>
|
||||||
|
<meta charset=utf-8>
|
||||||
|
<meta name=viewport content="width=device-width,initial-scale=1">
|
||||||
|
<meta name=description content>
|
||||||
|
<meta name=theme-color content="#FFFFFF">
|
||||||
|
<meta name=color-scheme content="light dark"><meta property="og:title" content="404 Page not found">
|
||||||
|
<meta property="og:description" content>
|
||||||
|
<meta property="og:type" content="website">
|
||||||
|
<meta property="og:url" content="https://syssecfsu.github.io/witty/404.html">
|
||||||
|
<title>404 Page not found | WiTTY</title>
|
||||||
|
<link rel=manifest href=https://syssecfsu.github.io/witty/manifest.json>
|
||||||
|
<link rel=icon href=https://syssecfsu.github.io/witty/favicon.png type=image/x-icon>
|
||||||
|
<link rel=stylesheet href=https://syssecfsu.github.io/witty/book.min.ce78b1b74b3a1081ebd0d36b0eb48d28d6d6f79be1ed3d41925fcc9a239d7a44.css integrity="sha256-znixt0s6EIHr0NNrDrSNKNbW95vh7T1Bkl/MmiOdekQ=" crossorigin=anonymous>
|
||||||
|
<script defer src=https://syssecfsu.github.io/witty/flexsearch.min.js></script>
|
||||||
|
<script defer src=https://syssecfsu.github.io/witty/en.search.min.c2e16a8098729d8520f5f13d7a4e43262b74a3ca3c2e8681edd14da9dc145e7e.js integrity="sha256-wuFqgJhynYUg9fE9ek5DJit0o8o8LoaB7dFNqdwUXn4=" crossorigin=anonymous></script>
|
||||||
|
<style>.not-found{text-align:center}.not-found h1{margin:.25em 0 0;opacity:.25;font-size:40vmin}</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main class="flex justify-center not-found">
|
||||||
|
<div>
|
||||||
|
<h1>404</h1>
|
||||||
|
<h2>Page Not Found</h2>
|
||||||
|
<h3>
|
||||||
|
<a href=https://syssecfsu.github.io/witty/>WiTTY</a>
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
76
docs/categories/index.html
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
<!doctype html><html lang=en-us dir=ltr>
|
||||||
|
<head>
|
||||||
|
<meta charset=utf-8>
|
||||||
|
<meta name=viewport content="width=device-width,initial-scale=1">
|
||||||
|
<meta name=description content>
|
||||||
|
<meta name=theme-color content="#FFFFFF">
|
||||||
|
<meta name=color-scheme content="light dark"><meta property="og:title" content="Categories">
|
||||||
|
<meta property="og:description" content>
|
||||||
|
<meta property="og:type" content="website">
|
||||||
|
<meta property="og:url" content="https://syssecfsu.github.io/witty/categories/">
|
||||||
|
<title>Categories | WiTTY</title>
|
||||||
|
<link rel=manifest href=https://syssecfsu.github.io/witty/manifest.json>
|
||||||
|
<link rel=icon href=https://syssecfsu.github.io/witty/favicon.png type=image/x-icon>
|
||||||
|
<link rel=stylesheet href=https://syssecfsu.github.io/witty/book.min.ce78b1b74b3a1081ebd0d36b0eb48d28d6d6f79be1ed3d41925fcc9a239d7a44.css integrity="sha256-znixt0s6EIHr0NNrDrSNKNbW95vh7T1Bkl/MmiOdekQ=" crossorigin=anonymous>
|
||||||
|
<script defer src=https://syssecfsu.github.io/witty/flexsearch.min.js></script>
|
||||||
|
<script defer src=https://syssecfsu.github.io/witty/en.search.min.c2e16a8098729d8520f5f13d7a4e43262b74a3ca3c2e8681edd14da9dc145e7e.js integrity="sha256-wuFqgJhynYUg9fE9ek5DJit0o8o8LoaB7dFNqdwUXn4=" crossorigin=anonymous></script>
|
||||||
|
<link rel=alternate type=application/rss+xml href=https://syssecfsu.github.io/witty/categories/index.xml title=WiTTY>
|
||||||
|
</head>
|
||||||
|
<body dir=ltr>
|
||||||
|
<input type=checkbox class="hidden toggle" id=menu-control>
|
||||||
|
<input type=checkbox class="hidden toggle" id=toc-control>
|
||||||
|
<main class="container flex">
|
||||||
|
<aside class=book-menu>
|
||||||
|
<div class=book-menu-content>
|
||||||
|
<nav>
|
||||||
|
<h2 class=book-brand>
|
||||||
|
<a class="flex align-center" href=https://syssecfsu.github.io/witty/><img src=https://syssecfsu.github.io/witty/static/img/logo.svg alt=Logo><span>WiTTY</span>
|
||||||
|
</a>
|
||||||
|
</h2>
|
||||||
|
<div class=book-search>
|
||||||
|
<input type=text id=book-search-input placeholder=Search aria-label=Search maxlength=64 data-hotkeys=s/>
|
||||||
|
<div class="book-search-spinner hidden"></div>
|
||||||
|
<ul id=book-search-results></ul>
|
||||||
|
</div>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<a href=https://syssecfsu.github.io/witty/docs/install/>Installation</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href=https://syssecfsu.github.io/witty/docs/ui/>User Interface</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href=https://syssecfsu.github.io/witty/docs/record/>Record Sessions</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href=https://syssecfsu.github.io/witty/docs/vm/>VirtualBox</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href=https://syssecfsu.github.io/witty/docs/about/>About</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
<script>(function(){var a=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(b){localStorage.setItem("menu.scrollTop",a.scrollTop)}),a.scrollTop=localStorage.getItem("menu.scrollTop")})()</script>
|
||||||
|
</div>
|
||||||
|
</aside>
|
||||||
|
<div class=book-page>
|
||||||
|
<header class=book-header>
|
||||||
|
<div class="flex align-center justify-between">
|
||||||
|
<label for=menu-control>
|
||||||
|
<img src=https://syssecfsu.github.io/witty/svg/menu.svg class=book-icon alt=Menu>
|
||||||
|
</label>
|
||||||
|
<strong>Categories</strong>
|
||||||
|
<label for=toc-control>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
<footer class=book-footer>
|
||||||
|
<div class="flex flex-wrap justify-between">
|
||||||
|
</div>
|
||||||
|
<script>(function(){function a(c){const a=window.getSelection(),b=document.createRange();b.selectNodeContents(c),a.removeAllRanges(),a.addRange(b)}document.querySelectorAll("pre code").forEach(b=>{b.addEventListener("click",function(c){a(b.parentElement),navigator.clipboard&&navigator.clipboard.writeText(b.parentElement.textContent)})})})()</script>
|
||||||
|
</footer>
|
||||||
|
<label for=menu-control class="hidden book-menu-overlay"></label>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
1
docs/categories/index.xml
Normal file
@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Categories on WiTTY</title><link>https://syssecfsu.github.io/witty/categories/</link><description>Recent content in Categories on WiTTY</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><atom:link href="https://syssecfsu.github.io/witty/categories/index.xml" rel="self" type="application/rss+xml"/></channel></rss>
|
1
docs/categories/page/1/index.html
Normal file
@ -0,0 +1 @@
|
|||||||
|
<!doctype html><html><head><title>https://syssecfsu.github.io/witty/categories/</title><link rel=canonical href=https://syssecfsu.github.io/witty/categories/><meta name=robots content="noindex"><meta charset=utf-8><meta http-equiv=refresh content="0; url=https://syssecfsu.github.io/witty/categories/"></head></html>
|
98
docs/docs/about/index.html
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
<!doctype html><html lang=en-us dir=ltr>
|
||||||
|
<head>
|
||||||
|
<meta charset=utf-8>
|
||||||
|
<meta name=viewport content="width=device-width,initial-scale=1">
|
||||||
|
<meta name=description content="About WiTTY # WiTTY is written in the go programming language, using the Gin web framework, gorilla/websocket, pty, and the wonderful xterm.js! The workflow is simple, the client initiates a terminal window (xterm.js) and creates a websocket with the server, which relays the data between pty and xterm.
|
||||||
|
The program has been tested on Linux, WSL2, Raspberry Pi 3B (Debian), and MacOSX using Google Chrome, Firefox, and Safari.
|
||||||
|
Most icons were provided by fontawesome under this license.">
|
||||||
|
<meta name=theme-color content="#FFFFFF">
|
||||||
|
<meta name=color-scheme content="light dark"><meta property="og:title" content="About">
|
||||||
|
<meta property="og:description" content="About WiTTY # WiTTY is written in the go programming language, using the Gin web framework, gorilla/websocket, pty, and the wonderful xterm.js! The workflow is simple, the client initiates a terminal window (xterm.js) and creates a websocket with the server, which relays the data between pty and xterm.
|
||||||
|
The program has been tested on Linux, WSL2, Raspberry Pi 3B (Debian), and MacOSX using Google Chrome, Firefox, and Safari.
|
||||||
|
Most icons were provided by fontawesome under this license.">
|
||||||
|
<meta property="og:type" content="article">
|
||||||
|
<meta property="og:url" content="https://syssecfsu.github.io/witty/docs/about/"><meta property="article:section" content="docs">
|
||||||
|
<meta property="article:modified_time" content="2022-02-06T20:15:40-05:00">
|
||||||
|
<title>About | WiTTY</title>
|
||||||
|
<link rel=manifest href=https://syssecfsu.github.io/witty/manifest.json>
|
||||||
|
<link rel=icon href=https://syssecfsu.github.io/witty/favicon.png type=image/x-icon>
|
||||||
|
<link rel=stylesheet href=https://syssecfsu.github.io/witty/book.min.ce78b1b74b3a1081ebd0d36b0eb48d28d6d6f79be1ed3d41925fcc9a239d7a44.css integrity="sha256-znixt0s6EIHr0NNrDrSNKNbW95vh7T1Bkl/MmiOdekQ=" crossorigin=anonymous>
|
||||||
|
<script defer src=https://syssecfsu.github.io/witty/flexsearch.min.js></script>
|
||||||
|
<script defer src=https://syssecfsu.github.io/witty/en.search.min.c2e16a8098729d8520f5f13d7a4e43262b74a3ca3c2e8681edd14da9dc145e7e.js integrity="sha256-wuFqgJhynYUg9fE9ek5DJit0o8o8LoaB7dFNqdwUXn4=" crossorigin=anonymous></script>
|
||||||
|
</head>
|
||||||
|
<body dir=ltr>
|
||||||
|
<input type=checkbox class="hidden toggle" id=menu-control>
|
||||||
|
<input type=checkbox class="hidden toggle" id=toc-control>
|
||||||
|
<main class="container flex">
|
||||||
|
<aside class=book-menu>
|
||||||
|
<div class=book-menu-content>
|
||||||
|
<nav>
|
||||||
|
<h2 class=book-brand>
|
||||||
|
<a class="flex align-center" href=https://syssecfsu.github.io/witty/><img src=https://syssecfsu.github.io/witty/static/img/logo.svg alt=Logo><span>WiTTY</span>
|
||||||
|
</a>
|
||||||
|
</h2>
|
||||||
|
<div class=book-search>
|
||||||
|
<input type=text id=book-search-input placeholder=Search aria-label=Search maxlength=64 data-hotkeys=s/>
|
||||||
|
<div class="book-search-spinner hidden"></div>
|
||||||
|
<ul id=book-search-results></ul>
|
||||||
|
</div>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<a href=https://syssecfsu.github.io/witty/docs/install/>Installation</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href=https://syssecfsu.github.io/witty/docs/ui/>User Interface</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href=https://syssecfsu.github.io/witty/docs/record/>Record Sessions</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href=https://syssecfsu.github.io/witty/docs/vm/>VirtualBox</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href=https://syssecfsu.github.io/witty/docs/about/ class=active>About</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
<script>(function(){var a=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(b){localStorage.setItem("menu.scrollTop",a.scrollTop)}),a.scrollTop=localStorage.getItem("menu.scrollTop")})()</script>
|
||||||
|
</div>
|
||||||
|
</aside>
|
||||||
|
<div class=book-page>
|
||||||
|
<header class=book-header>
|
||||||
|
<div class="flex align-center justify-between">
|
||||||
|
<label for=menu-control>
|
||||||
|
<img src=https://syssecfsu.github.io/witty/svg/menu.svg class=book-icon alt=Menu>
|
||||||
|
</label>
|
||||||
|
<strong>About</strong>
|
||||||
|
<label for=toc-control>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
<article class=markdown><h1 id=about-witty>
|
||||||
|
About WiTTY
|
||||||
|
<a class=anchor href=#about-witty>#</a>
|
||||||
|
</h1>
|
||||||
|
<p>WiTTY is written in the <a href=https://go.dev/>go programming language</a>, using the
|
||||||
|
<a href=https://github.com/gin-gonic/gin>Gin web framework</a>, <a href=https://github.com/gorilla/websocket>gorilla/websocket</a>, <a href=https://github.com/creack/pty>pty</a>, and the wonderful <a href=https://xtermjs.org/>xterm.js</a>!
|
||||||
|
The workflow is simple, the client initiates a terminal
|
||||||
|
window (xterm.js) and creates a websocket with the server, which relays the data between pty and xterm.</p>
|
||||||
|
<p>The program has been tested on Linux, WSL2, Raspberry Pi 3B (Debian), and MacOSX using Google Chrome, Firefox, and Safari.</p>
|
||||||
|
<p>Most icons were provided by <a href=https://fontawesome.com/>fontawesome</a> under this <a href=https://fontawesome.com/license>license</a>.</p>
|
||||||
|
</article>
|
||||||
|
<footer class=book-footer>
|
||||||
|
<div class="flex flex-wrap justify-between">
|
||||||
|
<div><a class="flex align-center" href=https://github.com/syssecfsu/witty-docs/commit/acce7b514f3f5fc0796bdbef318863e7707934fd title="Last modified by Zhi Wang | February 6, 2022" target=_blank rel=noopener>
|
||||||
|
<img src=https://syssecfsu.github.io/witty/svg/calendar.svg class=book-icon alt=Calendar>
|
||||||
|
<span>February 6, 2022</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script>(function(){function a(c){const a=window.getSelection(),b=document.createRange();b.selectNodeContents(c),a.removeAllRanges(),a.addRange(b)}document.querySelectorAll("pre code").forEach(b=>{b.addEventListener("click",function(c){a(b.parentElement),navigator.clipboard&&navigator.clipboard.writeText(b.parentElement.textContent)})})})()</script>
|
||||||
|
</footer>
|
||||||
|
<div class=book-comments>
|
||||||
|
</div>
|
||||||
|
<label for=menu-control class="hidden book-menu-overlay"></label>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
82
docs/docs/index.html
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
<!doctype html><html lang=en-us dir=ltr>
|
||||||
|
<head>
|
||||||
|
<meta charset=utf-8>
|
||||||
|
<meta name=viewport content="width=device-width,initial-scale=1">
|
||||||
|
<meta name=description content>
|
||||||
|
<meta name=theme-color content="#FFFFFF">
|
||||||
|
<meta name=color-scheme content="light dark"><meta property="og:title" content="Table of content">
|
||||||
|
<meta property="og:description" content>
|
||||||
|
<meta property="og:type" content="website">
|
||||||
|
<meta property="og:url" content="https://syssecfsu.github.io/witty/docs/">
|
||||||
|
<title>Table of content | WiTTY</title>
|
||||||
|
<link rel=manifest href=https://syssecfsu.github.io/witty/manifest.json>
|
||||||
|
<link rel=icon href=https://syssecfsu.github.io/witty/favicon.png type=image/x-icon>
|
||||||
|
<link rel=stylesheet href=https://syssecfsu.github.io/witty/book.min.ce78b1b74b3a1081ebd0d36b0eb48d28d6d6f79be1ed3d41925fcc9a239d7a44.css integrity="sha256-znixt0s6EIHr0NNrDrSNKNbW95vh7T1Bkl/MmiOdekQ=" crossorigin=anonymous>
|
||||||
|
<script defer src=https://syssecfsu.github.io/witty/flexsearch.min.js></script>
|
||||||
|
<script defer src=https://syssecfsu.github.io/witty/en.search.min.c2e16a8098729d8520f5f13d7a4e43262b74a3ca3c2e8681edd14da9dc145e7e.js integrity="sha256-wuFqgJhynYUg9fE9ek5DJit0o8o8LoaB7dFNqdwUXn4=" crossorigin=anonymous></script>
|
||||||
|
<link rel=alternate type=application/rss+xml href=https://syssecfsu.github.io/witty/docs/index.xml title=WiTTY>
|
||||||
|
</head>
|
||||||
|
<body dir=ltr>
|
||||||
|
<input type=checkbox class="hidden toggle" id=menu-control>
|
||||||
|
<input type=checkbox class="hidden toggle" id=toc-control>
|
||||||
|
<main class="container flex">
|
||||||
|
<aside class=book-menu>
|
||||||
|
<div class=book-menu-content>
|
||||||
|
<nav>
|
||||||
|
<h2 class=book-brand>
|
||||||
|
<a class="flex align-center" href=https://syssecfsu.github.io/witty/><img src=https://syssecfsu.github.io/witty/static/img/logo.svg alt=Logo><span>WiTTY</span>
|
||||||
|
</a>
|
||||||
|
</h2>
|
||||||
|
<div class=book-search>
|
||||||
|
<input type=text id=book-search-input placeholder=Search aria-label=Search maxlength=64 data-hotkeys=s/>
|
||||||
|
<div class="book-search-spinner hidden"></div>
|
||||||
|
<ul id=book-search-results></ul>
|
||||||
|
</div>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<a href=https://syssecfsu.github.io/witty/docs/install/>Installation</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href=https://syssecfsu.github.io/witty/docs/ui/>User Interface</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href=https://syssecfsu.github.io/witty/docs/record/>Record Sessions</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href=https://syssecfsu.github.io/witty/docs/vm/>VirtualBox</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href=https://syssecfsu.github.io/witty/docs/about/>About</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
<script>(function(){var a=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(b){localStorage.setItem("menu.scrollTop",a.scrollTop)}),a.scrollTop=localStorage.getItem("menu.scrollTop")})()</script>
|
||||||
|
</div>
|
||||||
|
</aside>
|
||||||
|
<div class=book-page>
|
||||||
|
<header class=book-header>
|
||||||
|
<div class="flex align-center justify-between">
|
||||||
|
<label for=menu-control>
|
||||||
|
<img src=https://syssecfsu.github.io/witty/svg/menu.svg class=book-icon alt=Menu>
|
||||||
|
</label>
|
||||||
|
<strong>Table of content</strong>
|
||||||
|
<label for=toc-control>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
<article class=markdown></article>
|
||||||
|
<footer class=book-footer>
|
||||||
|
<div class="flex flex-wrap justify-between">
|
||||||
|
<div><a class="flex align-center" href=https://github.com/syssecfsu/witty-docs/commit/cff6bb968e067e1c82a63ab27fefb6b6346a819e title="Last modified by Zhi Wang | February 6, 2022" target=_blank rel=noopener>
|
||||||
|
<img src=https://syssecfsu.github.io/witty/svg/calendar.svg class=book-icon alt=Calendar>
|
||||||
|
<span>February 6, 2022</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script>(function(){function a(c){const a=window.getSelection(),b=document.createRange();b.selectNodeContents(c),a.removeAllRanges(),a.addRange(b)}document.querySelectorAll("pre code").forEach(b=>{b.addEventListener("click",function(c){a(b.parentElement),navigator.clipboard&&navigator.clipboard.writeText(b.parentElement.textContent)})})})()</script>
|
||||||
|
</footer>
|
||||||
|
<label for=menu-control class="hidden book-menu-overlay"></label>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
12
docs/docs/index.xml
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Table of content on WiTTY</title><link>https://syssecfsu.github.io/witty/docs/</link><description>Recent content in Table of content on WiTTY</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><atom:link href="https://syssecfsu.github.io/witty/docs/index.xml" rel="self" type="application/rss+xml"/><item><title>Installation</title><link>https://syssecfsu.github.io/witty/docs/install/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://syssecfsu.github.io/witty/docs/install/</guid><description>Installation # WiTTY runs on Linux (ARM and x86), macOS, and WSL2 (Windows subsystem for Linux, basically Linux). You can install from the pre-built binary or from the source code.
|
||||||
|
From Binary Visit the release page of WiTTY at https://github.com/syssecfsu/witty/releases
|
||||||
|
Download the release for your system
|
||||||
|
Decompress the binary with the following command at selected location.
|
||||||
|
tar -xzvf witty_vx.x.x_xxx.tar.gz
|
||||||
|
For example, use tar -xzvf witty_v1.</description></item><item><title>User Interface</title><link>https://syssecfsu.github.io/witty/docs/ui/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://syssecfsu.github.io/witty/docs/ui/</guid><description>Sub-commands # Similar to git and apt, WiTTY uses sub-commands for its various functions. WiTTY currently supports the following sub-commands: adduser, deluser, listusers, replay, merge, run.
|
||||||
|
Sub-command Description adduser Add/update an authenticated user with their password deluser Delete an authenticated user listusers List all the authenticated users replay Replay a recorded session (set your terminal to 120x36 first) merge Merge several recorded sessions into one session run Run a specified CLI program when user connects with browser Some sub-commands have options.</description></item><item><title>Record Sessions</title><link>https://syssecfsu.github.io/witty/docs/record/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://syssecfsu.github.io/witty/docs/record/</guid><description>Record Sessions # You can record an ongoing session in the interactive terminal window.
|
||||||
|
Recorded sessions will be listed in the main window of WiTTY. You can click the button to rename a recorded session. By default, a recorded session is named based on its session ID and the current time, not very meaningful for human. Rename them to something easy to remember, such as task1, task2,&hellip;
|
||||||
|
WiTTY provides two sub-commands to merge and replay recorded sessions.</description></item><item><title>VirtualBox</title><link>https://syssecfsu.github.io/witty/docs/vm/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://syssecfsu.github.io/witty/docs/vm/</guid><description>Use WiTTY with SEED VM # The SEED labs provides a number of security hands-on labs. It is a popular security lab course taught at many universities. The SEED labs use VirtualBox to run its VMs (because VirtualBox is cross-platform.)
|
||||||
|
By default, the SEED VM uses only NAT-based network, which means that the VM can access the Internet but not the host machine (i.e., the machine that runs VirtualBox is called the host, and the VM is often called the guest.</description></item><item><title>About</title><link>https://syssecfsu.github.io/witty/docs/about/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://syssecfsu.github.io/witty/docs/about/</guid><description>About WiTTY # WiTTY is written in the go programming language, using the Gin web framework, gorilla/websocket, pty, and the wonderful xterm.js! The workflow is simple, the client initiates a terminal window (xterm.js) and creates a websocket with the server, which relays the data between pty and xterm.
|
||||||
|
The program has been tested on Linux, WSL2, Raspberry Pi 3B (Debian), and MacOSX using Google Chrome, Firefox, and Safari.
|
||||||
|
Most icons were provided by fontawesome under this license.</description></item></channel></rss>
|
189
docs/docs/install/index.html
Normal file
@ -0,0 +1,189 @@
|
|||||||
|
<!doctype html><html lang=en-us dir=ltr>
|
||||||
|
<head>
|
||||||
|
<meta charset=utf-8>
|
||||||
|
<meta name=viewport content="width=device-width,initial-scale=1">
|
||||||
|
<meta name=description content="Installation # WiTTY runs on Linux (ARM and x86), macOS, and WSL2 (Windows subsystem for Linux, basically Linux). You can install from the pre-built binary or from the source code.
|
||||||
|
From Binary Visit the release page of WiTTY at https://github.com/syssecfsu/witty/releases
|
||||||
|
Download the release for your system
|
||||||
|
Decompress the binary with the following command at selected location.
|
||||||
|
tar -xzvf witty_vx.x.x_xxx.tar.gz
|
||||||
|
For example, use tar -xzvf witty_v1.">
|
||||||
|
<meta name=theme-color content="#FFFFFF">
|
||||||
|
<meta name=color-scheme content="light dark"><meta property="og:title" content="Installation">
|
||||||
|
<meta property="og:description" content="Installation # WiTTY runs on Linux (ARM and x86), macOS, and WSL2 (Windows subsystem for Linux, basically Linux). You can install from the pre-built binary or from the source code.
|
||||||
|
From Binary Visit the release page of WiTTY at https://github.com/syssecfsu/witty/releases
|
||||||
|
Download the release for your system
|
||||||
|
Decompress the binary with the following command at selected location.
|
||||||
|
tar -xzvf witty_vx.x.x_xxx.tar.gz
|
||||||
|
For example, use tar -xzvf witty_v1.">
|
||||||
|
<meta property="og:type" content="article">
|
||||||
|
<meta property="og:url" content="https://syssecfsu.github.io/witty/docs/install/"><meta property="article:section" content="docs">
|
||||||
|
<meta property="article:modified_time" content="2022-02-17T08:49:10-05:00">
|
||||||
|
<title>Installation | WiTTY</title>
|
||||||
|
<link rel=manifest href=https://syssecfsu.github.io/witty/manifest.json>
|
||||||
|
<link rel=icon href=https://syssecfsu.github.io/witty/favicon.png type=image/x-icon>
|
||||||
|
<link rel=stylesheet href=https://syssecfsu.github.io/witty/book.min.ce78b1b74b3a1081ebd0d36b0eb48d28d6d6f79be1ed3d41925fcc9a239d7a44.css integrity="sha256-znixt0s6EIHr0NNrDrSNKNbW95vh7T1Bkl/MmiOdekQ=" crossorigin=anonymous>
|
||||||
|
<script defer src=https://syssecfsu.github.io/witty/flexsearch.min.js></script>
|
||||||
|
<script defer src=https://syssecfsu.github.io/witty/en.search.min.c2e16a8098729d8520f5f13d7a4e43262b74a3ca3c2e8681edd14da9dc145e7e.js integrity="sha256-wuFqgJhynYUg9fE9ek5DJit0o8o8LoaB7dFNqdwUXn4=" crossorigin=anonymous></script>
|
||||||
|
</head>
|
||||||
|
<body dir=ltr>
|
||||||
|
<input type=checkbox class="hidden toggle" id=menu-control>
|
||||||
|
<input type=checkbox class="hidden toggle" id=toc-control>
|
||||||
|
<main class="container flex">
|
||||||
|
<aside class=book-menu>
|
||||||
|
<div class=book-menu-content>
|
||||||
|
<nav>
|
||||||
|
<h2 class=book-brand>
|
||||||
|
<a class="flex align-center" href=https://syssecfsu.github.io/witty/><img src=https://syssecfsu.github.io/witty/static/img/logo.svg alt=Logo><span>WiTTY</span>
|
||||||
|
</a>
|
||||||
|
</h2>
|
||||||
|
<div class=book-search>
|
||||||
|
<input type=text id=book-search-input placeholder=Search aria-label=Search maxlength=64 data-hotkeys=s/>
|
||||||
|
<div class="book-search-spinner hidden"></div>
|
||||||
|
<ul id=book-search-results></ul>
|
||||||
|
</div>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<a href=https://syssecfsu.github.io/witty/docs/install/ class=active>Installation</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href=https://syssecfsu.github.io/witty/docs/ui/>User Interface</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href=https://syssecfsu.github.io/witty/docs/record/>Record Sessions</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href=https://syssecfsu.github.io/witty/docs/vm/>VirtualBox</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href=https://syssecfsu.github.io/witty/docs/about/>About</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
<script>(function(){var a=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(b){localStorage.setItem("menu.scrollTop",a.scrollTop)}),a.scrollTop=localStorage.getItem("menu.scrollTop")})()</script>
|
||||||
|
</div>
|
||||||
|
</aside>
|
||||||
|
<div class=book-page>
|
||||||
|
<header class=book-header>
|
||||||
|
<div class="flex align-center justify-between">
|
||||||
|
<label for=menu-control>
|
||||||
|
<img src=https://syssecfsu.github.io/witty/svg/menu.svg class=book-icon alt=Menu>
|
||||||
|
</label>
|
||||||
|
<strong>Installation</strong>
|
||||||
|
<label for=toc-control>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
<article class=markdown><h1 id=installation>
|
||||||
|
Installation
|
||||||
|
<a class=anchor href=#installation>#</a>
|
||||||
|
</h1>
|
||||||
|
<p>WiTTY runs on Linux (ARM and x86), macOS, and WSL2 (Windows subsystem for Linux, basically Linux). You can install from the pre-built binary or from the source code.</p>
|
||||||
|
<div class=book-tabs><input type=radio class=toggle name=tabs-install_method id=tabs-install_method-0 checked>
|
||||||
|
<label for=tabs-install_method-0>From Binary</label>
|
||||||
|
<div class="book-tabs-content markdown-inner"><ol>
|
||||||
|
<li>
|
||||||
|
<p>Visit the release page of WiTTY at <a href=https://github.com/syssecfsu/witty/releases>https://github.com/syssecfsu/witty/releases</a></p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>Download the release for your system</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>Decompress the binary with the following command at selected location.</p>
|
||||||
|
<p><code>tar -xzvf witty_vx.x.x_xxx.tar.gz</code></p>
|
||||||
|
<blockquote>
|
||||||
|
<p>For example, use <code>tar -xzvf witty_v1.1.1_linux_amd64.tar.gz</code> to decompress release v1.1.1 for Linux on AMD64.</p>
|
||||||
|
</blockquote>
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
</div><input type=radio class=toggle name=tabs-install_method id=tabs-install_method-1>
|
||||||
|
<label for=tabs-install_method-1>From Source Code</label>
|
||||||
|
<div class="book-tabs-content markdown-inner"><ol>
|
||||||
|
<li>
|
||||||
|
<p>Install the <a href=https://go.dev/>go</a> compiler. <strong>Make sure you have go 1.17 or higher.</strong></p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>Download the source code release and unzip it, or clone the repo</p>
|
||||||
|
<p><code>git clone https://github.com/syssecfsu/witty.git</code></p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>Go to the root directory of the source code and build the program with</p>
|
||||||
|
<p><code>./build.sh</code></p>
|
||||||
|
<p>This shell script will build WiTTY and copy the binary with other needed files to the <code>release/</code> directory. You can move the <code>release/</code> directory to other places you want. <strong>Run WiTTY from this directory.</strong></p>
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
<blockquote class="book-hint info">
|
||||||
|
macOS users can install go with <a href=https://brew.sh/>homebrew</a>. Note that macOS has its own version of golang installed. Do not remove it. Just add the path of newly installed golang at the beginning of the PATH environment variable.
|
||||||
|
</blockquote>
|
||||||
|
<blockquote class="book-hint danger">
|
||||||
|
WiTTY uses <a href=https://pkg.go.dev/embed>go:embed</a> to embed assets in the binary. Remember to re-build WiTTY after changing templates.
|
||||||
|
</blockquote>
|
||||||
|
</div></div>
|
||||||
|
<h1 id=post-installation-configuration>
|
||||||
|
Post-installation Configuration
|
||||||
|
<a class=anchor href=#post-installation-configuration>#</a>
|
||||||
|
</h1>
|
||||||
|
<ol>
|
||||||
|
<li>
|
||||||
|
<p>WiTTY uses TLS to protect its traffic. You can request a free <a href=https://letsencrypt.org/>Let’s Encrypt</a> cert or use a self-signed cert. Here is how to create a self-signed cert in the <code>tls</code> sub-directory:</p>
|
||||||
|
<p># Generate a private key for a curve</p>
|
||||||
|
<p><code>openssl ecparam -name prime256v1 -genkey -noout -out private-key.pem</code></p>
|
||||||
|
<p># Create a self-signed certificate</p>
|
||||||
|
<p><code>openssl req -new -x509 -key private-key.pem -out cert.pem -days 360</code></p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>Add a user to the user accounts, follow the instructions on screen to provide the password</p>
|
||||||
|
<p><code>./witty adduser <username></code></p>
|
||||||
|
<blockquote class="book-hint info">
|
||||||
|
WiTTY uses sub-commands for different functions. See details <a href=https://syssecfsu.github.io/witty/docs/ui/>here</a>
|
||||||
|
</blockquote>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>Start the server and specify the command-line (CLI) program to run when user connects.</p>
|
||||||
|
<p><code>./witty run bash</code></p>
|
||||||
|
<blockquote class="book-hint info">
|
||||||
|
<p>If so desired, you can disable user authenticate with <code>-n/-naked</code>, (not recommended). In the following example, WiTTY will run the <code>ls</code> command without user authentication:</p>
|
||||||
|
<p><code>./witty run -naked ls</code></p>
|
||||||
|
</blockquote>
|
||||||
|
<blockquote class="book-hint info">
|
||||||
|
WiTTY normally listens on port 8080. It can be overridden with the <code>-p/-port</code> option:
|
||||||
|
</blockquote>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>Connect to the server with your browser at port 8080 or the one specified in step 3, for example</p>
|
||||||
|
<p><code>https://<witty_server_ip>:8080</code></p>
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
<h1 id=example-use-case>
|
||||||
|
Example Use Case
|
||||||
|
<a class=anchor href=#example-use-case>#</a>
|
||||||
|
</h1>
|
||||||
|
<p>WiTTY doesn’t support Windows because Windows doesn’t have <a href=https://en.wikipedia.org/wiki/Pseudoterminal>PTY</a>. You can still use WiTTY to access Windows terminal through a proxy.</p>
|
||||||
|
<p>Here is how to do it. We can run WiTTY on a Raspberry Pi running <a href=https://www.raspberrypi.com/software/>Raspbian</a> (at address <code>192.168.1.2</code>). When a user connects via browser to <code>https://192.168.1.2:9000</code>, WiTTY will start a new <code>ssh</code> session to the Windows machine (at address <code>192.168.1.3</code>) running a SSH server. The command to run WiTTY on the RPI is as follows:</p>
|
||||||
|
<p><code>./witty run -p 9000 ssh 192.168.1.3 -l user_name</code></p>
|
||||||
|
<p>The user can use any compatible browsers, such as that on a phone, to connect to the Windows machine without install a SSH client.</p>
|
||||||
|
<script src=https://syssecfsu.github.io/witty/mermaid.min.js></script>
|
||||||
|
<script>mermaid.initialize({flowchart:{useMaxWidth:!0},theme:"default"})</script>
|
||||||
|
<p class=mermaid>
|
||||||
|
graph LR
|
||||||
|
A[user on a phone] -- browser --> B[RPI: 192.168.1.2 <br> runs WiTTY]
|
||||||
|
B -- ssh --> C[Windows: 192.168.1.3 <br> runs SSH server ]
|
||||||
|
</p>
|
||||||
|
</article>
|
||||||
|
<footer class=book-footer>
|
||||||
|
<div class="flex flex-wrap justify-between">
|
||||||
|
<div><a class="flex align-center" href=https://github.com/syssecfsu/witty-docs/commit/8564aedad71b12c1f7afcb7b5501b9a818f6d3bb title="Last modified by Zhi Wang | February 17, 2022" target=_blank rel=noopener>
|
||||||
|
<img src=https://syssecfsu.github.io/witty/svg/calendar.svg class=book-icon alt=Calendar>
|
||||||
|
<span>February 17, 2022</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script>(function(){function a(c){const a=window.getSelection(),b=document.createRange();b.selectNodeContents(c),a.removeAllRanges(),a.addRange(b)}document.querySelectorAll("pre code").forEach(b=>{b.addEventListener("click",function(c){a(b.parentElement),navigator.clipboard&&navigator.clipboard.writeText(b.parentElement.textContent)})})})()</script>
|
||||||
|
</footer>
|
||||||
|
<div class=book-comments>
|
||||||
|
</div>
|
||||||
|
<label for=menu-control class="hidden book-menu-overlay"></label>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
110
docs/docs/record/index.html
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
<!doctype html><html lang=en-us dir=ltr>
|
||||||
|
<head>
|
||||||
|
<meta charset=utf-8>
|
||||||
|
<meta name=viewport content="width=device-width,initial-scale=1">
|
||||||
|
<meta name=description content="Record Sessions # You can record an ongoing session in the interactive terminal window.
|
||||||
|
Recorded sessions will be listed in the main window of WiTTY. You can click the button to rename a recorded session. By default, a recorded session is named based on its session ID and the current time, not very meaningful for human. Rename them to something easy to remember, such as task1, task2,…
|
||||||
|
WiTTY provides two sub-commands to merge and replay recorded sessions.">
|
||||||
|
<meta name=theme-color content="#FFFFFF">
|
||||||
|
<meta name=color-scheme content="light dark"><meta property="og:title" content="Record Sessions">
|
||||||
|
<meta property="og:description" content="Record Sessions # You can record an ongoing session in the interactive terminal window.
|
||||||
|
Recorded sessions will be listed in the main window of WiTTY. You can click the button to rename a recorded session. By default, a recorded session is named based on its session ID and the current time, not very meaningful for human. Rename them to something easy to remember, such as task1, task2,…
|
||||||
|
WiTTY provides two sub-commands to merge and replay recorded sessions.">
|
||||||
|
<meta property="og:type" content="article">
|
||||||
|
<meta property="og:url" content="https://syssecfsu.github.io/witty/docs/record/"><meta property="article:section" content="docs">
|
||||||
|
<meta property="article:modified_time" content="2022-02-07T21:17:14-05:00">
|
||||||
|
<title>Record Sessions | WiTTY</title>
|
||||||
|
<link rel=manifest href=https://syssecfsu.github.io/witty/manifest.json>
|
||||||
|
<link rel=icon href=https://syssecfsu.github.io/witty/favicon.png type=image/x-icon>
|
||||||
|
<link rel=stylesheet href=https://syssecfsu.github.io/witty/book.min.ce78b1b74b3a1081ebd0d36b0eb48d28d6d6f79be1ed3d41925fcc9a239d7a44.css integrity="sha256-znixt0s6EIHr0NNrDrSNKNbW95vh7T1Bkl/MmiOdekQ=" crossorigin=anonymous>
|
||||||
|
<script defer src=https://syssecfsu.github.io/witty/flexsearch.min.js></script>
|
||||||
|
<script defer src=https://syssecfsu.github.io/witty/en.search.min.c2e16a8098729d8520f5f13d7a4e43262b74a3ca3c2e8681edd14da9dc145e7e.js integrity="sha256-wuFqgJhynYUg9fE9ek5DJit0o8o8LoaB7dFNqdwUXn4=" crossorigin=anonymous></script>
|
||||||
|
</head>
|
||||||
|
<body dir=ltr>
|
||||||
|
<input type=checkbox class="hidden toggle" id=menu-control>
|
||||||
|
<input type=checkbox class="hidden toggle" id=toc-control>
|
||||||
|
<main class="container flex">
|
||||||
|
<aside class=book-menu>
|
||||||
|
<div class=book-menu-content>
|
||||||
|
<nav>
|
||||||
|
<h2 class=book-brand>
|
||||||
|
<a class="flex align-center" href=https://syssecfsu.github.io/witty/><img src=https://syssecfsu.github.io/witty/static/img/logo.svg alt=Logo><span>WiTTY</span>
|
||||||
|
</a>
|
||||||
|
</h2>
|
||||||
|
<div class=book-search>
|
||||||
|
<input type=text id=book-search-input placeholder=Search aria-label=Search maxlength=64 data-hotkeys=s/>
|
||||||
|
<div class="book-search-spinner hidden"></div>
|
||||||
|
<ul id=book-search-results></ul>
|
||||||
|
</div>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<a href=https://syssecfsu.github.io/witty/docs/install/>Installation</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href=https://syssecfsu.github.io/witty/docs/ui/>User Interface</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href=https://syssecfsu.github.io/witty/docs/record/ class=active>Record Sessions</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href=https://syssecfsu.github.io/witty/docs/vm/>VirtualBox</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href=https://syssecfsu.github.io/witty/docs/about/>About</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
<script>(function(){var a=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(b){localStorage.setItem("menu.scrollTop",a.scrollTop)}),a.scrollTop=localStorage.getItem("menu.scrollTop")})()</script>
|
||||||
|
</div>
|
||||||
|
</aside>
|
||||||
|
<div class=book-page>
|
||||||
|
<header class=book-header>
|
||||||
|
<div class="flex align-center justify-between">
|
||||||
|
<label for=menu-control>
|
||||||
|
<img src=https://syssecfsu.github.io/witty/svg/menu.svg class=book-icon alt=Menu>
|
||||||
|
</label>
|
||||||
|
<strong>Record Sessions</strong>
|
||||||
|
<label for=toc-control>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
<article class=markdown><h1 id=record-sessions>
|
||||||
|
Record Sessions
|
||||||
|
<a class=anchor href=#record-sessions>#</a>
|
||||||
|
</h1>
|
||||||
|
<p>You can record an ongoing session in the interactive terminal window.</p>
|
||||||
|
<p><img src=https://syssecfsu.github.io/witty/static/img/interactive.png alt=img></p>
|
||||||
|
<p>Recorded sessions will be listed in the main window of WiTTY. You can click the <img src=https://syssecfsu.github.io/witty/static/img/edit.svg width=16px> button to rename a recorded session. By default, a recorded session is named based on its session ID and the current time, not very meaningful for human. Rename them to something easy to remember, such as <code>task1</code>, <code>task2</code>,…</p>
|
||||||
|
<p><img src=https://syssecfsu.github.io/witty/static/img/rename.png alt=img></p>
|
||||||
|
<p>WiTTY provides two sub-commands to merge and replay recorded sessions.</p>
|
||||||
|
<ul>
|
||||||
|
<li><code>witty merge -o <output_file> <record1> <record2> ...</code></li>
|
||||||
|
<li><code>witty replay -w <wait_time> <recorded_session></code></li>
|
||||||
|
</ul>
|
||||||
|
<p>Recorded sessions often have long delay between outputs. You can set <code>wait_time</code> of the <code>replay</code> command to limit the maximum wait time between outputs, to speed up the replay.</p>
|
||||||
|
<p>The following screenshot shows how to use <code>witty merge</code> to merge three recorded sessions into <code>alltasks.scr</code>.</p>
|
||||||
|
<p><img src=https://syssecfsu.github.io/witty/static/img/merge.png alt=img></p>
|
||||||
|
<blockquote class="book-hint info">
|
||||||
|
The intended use of this is to record a separate session for each individual task, rename and merge them into a final session for submission to a project.
|
||||||
|
</blockquote>
|
||||||
|
<blockquote class="book-hint info">
|
||||||
|
All the recorded sessions are located under the <code>records</code> directory.
|
||||||
|
</blockquote>
|
||||||
|
</article>
|
||||||
|
<footer class=book-footer>
|
||||||
|
<div class="flex flex-wrap justify-between">
|
||||||
|
<div><a class="flex align-center" href=https://github.com/syssecfsu/witty-docs/commit/8fbf423e33318bbb3ab7eaf94c82ccced9ff7470 title="Last modified by Zhi Wang | February 7, 2022" target=_blank rel=noopener>
|
||||||
|
<img src=https://syssecfsu.github.io/witty/svg/calendar.svg class=book-icon alt=Calendar>
|
||||||
|
<span>February 7, 2022</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script>(function(){function a(c){const a=window.getSelection(),b=document.createRange();b.selectNodeContents(c),a.removeAllRanges(),a.addRange(b)}document.querySelectorAll("pre code").forEach(b=>{b.addEventListener("click",function(c){a(b.parentElement),navigator.clipboard&&navigator.clipboard.writeText(b.parentElement.textContent)})})})()</script>
|
||||||
|
</footer>
|
||||||
|
<div class=book-comments>
|
||||||
|
</div>
|
||||||
|
<label for=menu-control class="hidden book-menu-overlay"></label>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
165
docs/docs/ui/index.html
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
<!doctype html><html lang=en-us dir=ltr>
|
||||||
|
<head>
|
||||||
|
<meta charset=utf-8>
|
||||||
|
<meta name=viewport content="width=device-width,initial-scale=1">
|
||||||
|
<meta name=description content="Sub-commands # Similar to git and apt, WiTTY uses sub-commands for its various functions. WiTTY currently supports the following sub-commands: adduser, deluser, listusers, replay, merge, run.
|
||||||
|
Sub-command Description adduser Add/update an authenticated user with their password deluser Delete an authenticated user listusers List all the authenticated users replay Replay a recorded session (set your terminal to 120x36 first) merge Merge several recorded sessions into one session run Run a specified CLI program when user connects with browser Some sub-commands have options.">
|
||||||
|
<meta name=theme-color content="#FFFFFF">
|
||||||
|
<meta name=color-scheme content="light dark"><meta property="og:title" content="User Interface">
|
||||||
|
<meta property="og:description" content="Sub-commands # Similar to git and apt, WiTTY uses sub-commands for its various functions. WiTTY currently supports the following sub-commands: adduser, deluser, listusers, replay, merge, run.
|
||||||
|
Sub-command Description adduser Add/update an authenticated user with their password deluser Delete an authenticated user listusers List all the authenticated users replay Replay a recorded session (set your terminal to 120x36 first) merge Merge several recorded sessions into one session run Run a specified CLI program when user connects with browser Some sub-commands have options.">
|
||||||
|
<meta property="og:type" content="article">
|
||||||
|
<meta property="og:url" content="https://syssecfsu.github.io/witty/docs/ui/"><meta property="article:section" content="docs">
|
||||||
|
<meta property="article:modified_time" content="2022-02-07T21:17:14-05:00">
|
||||||
|
<title>User Interface | WiTTY</title>
|
||||||
|
<link rel=manifest href=https://syssecfsu.github.io/witty/manifest.json>
|
||||||
|
<link rel=icon href=https://syssecfsu.github.io/witty/favicon.png type=image/x-icon>
|
||||||
|
<link rel=stylesheet href=https://syssecfsu.github.io/witty/book.min.ce78b1b74b3a1081ebd0d36b0eb48d28d6d6f79be1ed3d41925fcc9a239d7a44.css integrity="sha256-znixt0s6EIHr0NNrDrSNKNbW95vh7T1Bkl/MmiOdekQ=" crossorigin=anonymous>
|
||||||
|
<script defer src=https://syssecfsu.github.io/witty/flexsearch.min.js></script>
|
||||||
|
<script defer src=https://syssecfsu.github.io/witty/en.search.min.c2e16a8098729d8520f5f13d7a4e43262b74a3ca3c2e8681edd14da9dc145e7e.js integrity="sha256-wuFqgJhynYUg9fE9ek5DJit0o8o8LoaB7dFNqdwUXn4=" crossorigin=anonymous></script>
|
||||||
|
</head>
|
||||||
|
<body dir=ltr>
|
||||||
|
<input type=checkbox class="hidden toggle" id=menu-control>
|
||||||
|
<input type=checkbox class="hidden toggle" id=toc-control>
|
||||||
|
<main class="container flex">
|
||||||
|
<aside class=book-menu>
|
||||||
|
<div class=book-menu-content>
|
||||||
|
<nav>
|
||||||
|
<h2 class=book-brand>
|
||||||
|
<a class="flex align-center" href=https://syssecfsu.github.io/witty/><img src=https://syssecfsu.github.io/witty/static/img/logo.svg alt=Logo><span>WiTTY</span>
|
||||||
|
</a>
|
||||||
|
</h2>
|
||||||
|
<div class=book-search>
|
||||||
|
<input type=text id=book-search-input placeholder=Search aria-label=Search maxlength=64 data-hotkeys=s/>
|
||||||
|
<div class="book-search-spinner hidden"></div>
|
||||||
|
<ul id=book-search-results></ul>
|
||||||
|
</div>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<a href=https://syssecfsu.github.io/witty/docs/install/>Installation</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href=https://syssecfsu.github.io/witty/docs/ui/ class=active>User Interface</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href=https://syssecfsu.github.io/witty/docs/record/>Record Sessions</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href=https://syssecfsu.github.io/witty/docs/vm/>VirtualBox</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href=https://syssecfsu.github.io/witty/docs/about/>About</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
<script>(function(){var a=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(b){localStorage.setItem("menu.scrollTop",a.scrollTop)}),a.scrollTop=localStorage.getItem("menu.scrollTop")})()</script>
|
||||||
|
</div>
|
||||||
|
</aside>
|
||||||
|
<div class=book-page>
|
||||||
|
<header class=book-header>
|
||||||
|
<div class="flex align-center justify-between">
|
||||||
|
<label for=menu-control>
|
||||||
|
<img src=https://syssecfsu.github.io/witty/svg/menu.svg class=book-icon alt=Menu>
|
||||||
|
</label>
|
||||||
|
<strong>User Interface</strong>
|
||||||
|
<label for=toc-control>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
<article class=markdown><h1 id=sub-commands>
|
||||||
|
Sub-commands
|
||||||
|
<a class=anchor href=#sub-commands>#</a>
|
||||||
|
</h1>
|
||||||
|
<p>Similar to <code>git</code> and <code>apt</code>, WiTTY uses sub-commands for its various functions. WiTTY currently supports the following sub-commands: <code>adduser</code>, <code>deluser</code>, <code>listusers</code>, <code>replay</code>, <code>merge</code>, <code>run</code>.</p>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th style=text-align:center>Sub-command</th>
|
||||||
|
<th>Description</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td style=text-align:center><code>adduser</code></td>
|
||||||
|
<td>Add/update an authenticated user with their password</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td style=text-align:center><code>deluser</code></td>
|
||||||
|
<td>Delete an authenticated user</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td style=text-align:center><code>listusers</code></td>
|
||||||
|
<td>List all the authenticated users</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td style=text-align:center><code>replay</code></td>
|
||||||
|
<td>Replay a recorded session (set your terminal to 120x36 first)</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td style=text-align:center><code>merge</code></td>
|
||||||
|
<td>Merge several recorded sessions into one session</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td style=text-align:center><code>run</code></td>
|
||||||
|
<td>Run a specified CLI program when user connects with browser</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<p>Some sub-commands have options. Use <code>-h</code> to find out more. e.g.,</p>
|
||||||
|
<pre tabindex=0><code>$ ./witty run -h
|
||||||
|
Usage of run:
|
||||||
|
-n Run WiTTY without user authentication
|
||||||
|
-naked
|
||||||
|
Run WiTTY without user authentication
|
||||||
|
-p uint
|
||||||
|
Port number to listen on (default 8080)
|
||||||
|
-port uint
|
||||||
|
Port number to listen on (default 8080)
|
||||||
|
-w uint
|
||||||
|
Max wait time between outputs (default 1000)
|
||||||
|
-wait uint
|
||||||
|
Max wait time between outputs (default 1000)
|
||||||
|
</code></pre><h1 id=user-authentication>
|
||||||
|
User Authentication
|
||||||
|
<a class=anchor href=#user-authentication>#</a>
|
||||||
|
</h1>
|
||||||
|
<p>WiTTY uses username/password based authentication. The user database is stored in <code>user.db</code> under the main directory of WiTTY. The passwords are salted with 64 bytes of random characters and then hashed using SHA256. In addition, passwords must be 12 bytes or longer. WiTTY provides three sub-commands to manage <code>user.db</code>.</p>
|
||||||
|
<ul>
|
||||||
|
<li><code>witty adduser <username></code></li>
|
||||||
|
<li><code>witty deluser <username></code></li>
|
||||||
|
<li><code>witty listusers</code></li>
|
||||||
|
</ul>
|
||||||
|
<p>They are pretty self-explanatory. Just follow the instructions on screen.</p>
|
||||||
|
<h1 id=web-interface-witty-run>
|
||||||
|
Web Interface (witty run)
|
||||||
|
<a class=anchor href=#web-interface-witty-run>#</a>
|
||||||
|
</h1>
|
||||||
|
<p>The web interface of WiTTY is mostly self-explanatory. After login, the user is presented with the main interface, as shown in this screenshot:</p>
|
||||||
|
<p><img src=https://syssecfsu.github.io/witty/static/img/main.png alt=img></p>
|
||||||
|
<p>There are two tabs that list live and recorded sessions, respectively. You can click <code>New Session</code> to create a new interactive session, which opens in a new browser tab. The main window will list the newly created interactive session similar to the follows:</p>
|
||||||
|
<p><img src=https://syssecfsu.github.io/witty/static/img/main2.png alt=img></p>
|
||||||
|
<p>WiTTY randomly names an interactive session as its unique ID. You can click the <img src=https://syssecfsu.github.io/witty/static/img/view.svg width=16px> icon of an interactive session to open a read-only view of that session.</p>
|
||||||
|
<p>On the interactive terminal window, you can record an ongoing session by clicking the <code>record</code> button.</p>
|
||||||
|
<p><img src=https://syssecfsu.github.io/witty/static/img/interactive.png alt=img></p>
|
||||||
|
<p>After a session has been recorded, the main window’s <code>Recorded Sessions</code> will list the records, as shown below. You can replay <img src=https://syssecfsu.github.io/witty/static/img/play.svg width=16px>, download <img src=https://syssecfsu.github.io/witty/static/img/download.svg width=16px>, rename <img src=https://syssecfsu.github.io/witty/static/img/edit.svg width=16px>, and delete <img src=https://syssecfsu.github.io/witty/static/img/delete.svg width=16px> recorded sessions.</p>
|
||||||
|
<p><img src=https://syssecfsu.github.io/witty/static/img/main3.png alt=img></p>
|
||||||
|
<p>Here is a screencast where we replay a recorded session that updates the <a href=https://pi-hole.net/>pi-hole</a> system. You can fully control the playback using the progress bar. Clicking on the progress bar stops the replay and fast-forwards (or fast-backwards) the screen to that location. You can scroll the screen to view the history when replay is stopped and resume the replay.</p>
|
||||||
|
<p><img src=https://syssecfsu.github.io/witty/static/img/replay.gif alt=img></p>
|
||||||
|
</article>
|
||||||
|
<footer class=book-footer>
|
||||||
|
<div class="flex flex-wrap justify-between">
|
||||||
|
<div><a class="flex align-center" href=https://github.com/syssecfsu/witty-docs/commit/8fbf423e33318bbb3ab7eaf94c82ccced9ff7470 title="Last modified by Zhi Wang | February 7, 2022" target=_blank rel=noopener>
|
||||||
|
<img src=https://syssecfsu.github.io/witty/svg/calendar.svg class=book-icon alt=Calendar>
|
||||||
|
<span>February 7, 2022</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script>(function(){function a(c){const a=window.getSelection(),b=document.createRange();b.selectNodeContents(c),a.removeAllRanges(),a.addRange(b)}document.querySelectorAll("pre code").forEach(b=>{b.addEventListener("click",function(c){a(b.parentElement),navigator.clipboard&&navigator.clipboard.writeText(b.parentElement.textContent)})})})()</script>
|
||||||
|
</footer>
|
||||||
|
<div class=book-comments>
|
||||||
|
</div>
|
||||||
|
<label for=menu-control class="hidden book-menu-overlay"></label>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
100
docs/docs/vm/index.html
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
<!doctype html><html lang=en-us dir=ltr>
|
||||||
|
<head>
|
||||||
|
<meta charset=utf-8>
|
||||||
|
<meta name=viewport content="width=device-width,initial-scale=1">
|
||||||
|
<meta name=description content="Use WiTTY with SEED VM # The SEED labs provides a number of security hands-on labs. It is a popular security lab course taught at many universities. The SEED labs use VirtualBox to run its VMs (because VirtualBox is cross-platform.)
|
||||||
|
By default, the SEED VM uses only NAT-based network, which means that the VM can access the Internet but not the host machine (i.e., the machine that runs VirtualBox is called the host, and the VM is often called the guest.">
|
||||||
|
<meta name=theme-color content="#FFFFFF">
|
||||||
|
<meta name=color-scheme content="light dark"><meta property="og:title" content="VirtualBox">
|
||||||
|
<meta property="og:description" content="Use WiTTY with SEED VM # The SEED labs provides a number of security hands-on labs. It is a popular security lab course taught at many universities. The SEED labs use VirtualBox to run its VMs (because VirtualBox is cross-platform.)
|
||||||
|
By default, the SEED VM uses only NAT-based network, which means that the VM can access the Internet but not the host machine (i.e., the machine that runs VirtualBox is called the host, and the VM is often called the guest.">
|
||||||
|
<meta property="og:type" content="article">
|
||||||
|
<meta property="og:url" content="https://syssecfsu.github.io/witty/docs/vm/"><meta property="article:section" content="docs">
|
||||||
|
<meta property="article:modified_time" content="2022-02-07T12:30:17-05:00">
|
||||||
|
<title>VirtualBox | WiTTY</title>
|
||||||
|
<link rel=manifest href=https://syssecfsu.github.io/witty/manifest.json>
|
||||||
|
<link rel=icon href=https://syssecfsu.github.io/witty/favicon.png type=image/x-icon>
|
||||||
|
<link rel=stylesheet href=https://syssecfsu.github.io/witty/book.min.ce78b1b74b3a1081ebd0d36b0eb48d28d6d6f79be1ed3d41925fcc9a239d7a44.css integrity="sha256-znixt0s6EIHr0NNrDrSNKNbW95vh7T1Bkl/MmiOdekQ=" crossorigin=anonymous>
|
||||||
|
<script defer src=https://syssecfsu.github.io/witty/flexsearch.min.js></script>
|
||||||
|
<script defer src=https://syssecfsu.github.io/witty/en.search.min.c2e16a8098729d8520f5f13d7a4e43262b74a3ca3c2e8681edd14da9dc145e7e.js integrity="sha256-wuFqgJhynYUg9fE9ek5DJit0o8o8LoaB7dFNqdwUXn4=" crossorigin=anonymous></script>
|
||||||
|
</head>
|
||||||
|
<body dir=ltr>
|
||||||
|
<input type=checkbox class="hidden toggle" id=menu-control>
|
||||||
|
<input type=checkbox class="hidden toggle" id=toc-control>
|
||||||
|
<main class="container flex">
|
||||||
|
<aside class=book-menu>
|
||||||
|
<div class=book-menu-content>
|
||||||
|
<nav>
|
||||||
|
<h2 class=book-brand>
|
||||||
|
<a class="flex align-center" href=https://syssecfsu.github.io/witty/><img src=https://syssecfsu.github.io/witty/static/img/logo.svg alt=Logo><span>WiTTY</span>
|
||||||
|
</a>
|
||||||
|
</h2>
|
||||||
|
<div class=book-search>
|
||||||
|
<input type=text id=book-search-input placeholder=Search aria-label=Search maxlength=64 data-hotkeys=s/>
|
||||||
|
<div class="book-search-spinner hidden"></div>
|
||||||
|
<ul id=book-search-results></ul>
|
||||||
|
</div>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<a href=https://syssecfsu.github.io/witty/docs/install/>Installation</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href=https://syssecfsu.github.io/witty/docs/ui/>User Interface</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href=https://syssecfsu.github.io/witty/docs/record/>Record Sessions</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href=https://syssecfsu.github.io/witty/docs/vm/ class=active>VirtualBox</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href=https://syssecfsu.github.io/witty/docs/about/>About</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
<script>(function(){var a=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(b){localStorage.setItem("menu.scrollTop",a.scrollTop)}),a.scrollTop=localStorage.getItem("menu.scrollTop")})()</script>
|
||||||
|
</div>
|
||||||
|
</aside>
|
||||||
|
<div class=book-page>
|
||||||
|
<header class=book-header>
|
||||||
|
<div class="flex align-center justify-between">
|
||||||
|
<label for=menu-control>
|
||||||
|
<img src=https://syssecfsu.github.io/witty/svg/menu.svg class=book-icon alt=Menu>
|
||||||
|
</label>
|
||||||
|
<strong>VirtualBox</strong>
|
||||||
|
<label for=toc-control>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
<article class=markdown><h1 id=use-witty-with-seed-vm>
|
||||||
|
Use WiTTY with SEED VM
|
||||||
|
<a class=anchor href=#use-witty-with-seed-vm>#</a>
|
||||||
|
</h1>
|
||||||
|
<p>The <a href=https://seedsecuritylabs.org/Labs_20.04/>SEED labs</a> provides a number of security hands-on labs. It is a popular security lab course taught at many universities. The SEED labs use <a href=https://www.virtualbox.org/>VirtualBox</a> to run its VMs (because VirtualBox is cross-platform.)</p>
|
||||||
|
<p>By default, the SEED VM uses only NAT-based network, which means that the VM can access the Internet but not the host machine (i.e., the machine that runs VirtualBox is called the host, and the VM is often called the guest.) We need to add a second, host-only network adaptor in order to run WiTTY in the guest and access WiTTY from a browser in the host.</p>
|
||||||
|
<p>To do that, first open the <code>Host Network Manager</code> and create a host network if there is not one already, as shown below (on macOS.):
|
||||||
|
<img src=https://syssecfsu.github.io/witty/static/img/host.png alt=img></p>
|
||||||
|
<p>Then, open the setting for the SEED VM, go to the Network setting, and enable the second adaptor, choose the <code>host-only</code> adaptor. Leave the first adaptor as is.</p>
|
||||||
|
<p><img src=https://syssecfsu.github.io/witty/static/img/adapter2.png alt=img></p>
|
||||||
|
<p>After this, start the VM and list all the adaptors using the command <code>ifconfig</code> in a terminal. Look for the adaptor with an IP address starting with <code>192.168.</code>. You should be able to ssh into the guest using this IP address from the host. Follow the instruction <a href=https://syssecfsu.github.io/witty/docs/install/>here</a> to install and use WiTTY.</p>
|
||||||
|
<blockquote class="book-hint info">
|
||||||
|
If the host runs Windows, make sure virtualbox host adapter is not disabled in the Windows network settings if VirtualBox cannot find the host-only Ethernet adapter.
|
||||||
|
</blockquote>
|
||||||
|
</article>
|
||||||
|
<footer class=book-footer>
|
||||||
|
<div class="flex flex-wrap justify-between">
|
||||||
|
<div><a class="flex align-center" href=https://github.com/syssecfsu/witty-docs/commit/924a38a5d2aae36483f97d8669c89b57a01031de title="Last modified by Zhi Wang | February 7, 2022" target=_blank rel=noopener>
|
||||||
|
<img src=https://syssecfsu.github.io/witty/svg/calendar.svg class=book-icon alt=Calendar>
|
||||||
|
<span>February 7, 2022</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script>(function(){function a(c){const a=window.getSelection(),b=document.createRange();b.selectNodeContents(c),a.removeAllRanges(),a.addRange(b)}document.querySelectorAll("pre code").forEach(b=>{b.addEventListener("click",function(c){a(b.parentElement),navigator.clipboard&&navigator.clipboard.writeText(b.parentElement.textContent)})})})()</script>
|
||||||
|
</footer>
|
||||||
|
<div class=book-comments>
|
||||||
|
</div>
|
||||||
|
<label for=menu-control class="hidden book-menu-overlay"></label>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -0,0 +1 @@
|
|||||||
|
'use strict';(function(){const g='/en.search-data.min.6b854d792c3149a3e9d7fea498d4a129d5bb011901e75102f72de2c52e379737.json',h=Object.assign({cache:!0},{doc:{id:'id',field:['title','content'],store:['title','href','section']}}),a=document.querySelector('#book-search-input'),b=document.querySelector('#book-search-results');if(!a)return;a.addEventListener('focus',c),a.addEventListener('keyup',d),document.addEventListener('keypress',e);function e(b){if(b.target.value!==void 0)return;if(a===document.activeElement)return;const c=String.fromCharCode(b.charCode);if(!f(c))return;a.focus(),b.preventDefault()}function f(b){const c=a.getAttribute('data-hotkeys')||'';return c.indexOf(b)>=0}function c(){a.removeEventListener('focus',c),a.required=!0,fetch(g).then(a=>a.json()).then(a=>{window.bookSearchIndex=FlexSearch.create('balance',h),window.bookSearchIndex.add(a)}).then(()=>a.required=!1).then(d)}function d(){while(b.firstChild)b.removeChild(b.firstChild);if(!a.value)return;const c=window.bookSearchIndex.search(a.value,10);c.forEach(function(a){const c=i('<li><a href></a><small></small></li>'),d=c.querySelector('a'),e=c.querySelector('small');d.href=a.href,d.textContent=a.title,e.textContent=a.section,b.appendChild(c)})}function i(b){const a=document.createElement('div');return a.innerHTML=b,a.firstChild}})()
|
BIN
docs/favicon.png
Normal file
After Width: | Height: | Size: 109 B |
1
docs/favicon.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M3 18h12v-2H3v2zM3 6v2h18V6H3zm0 7h18v-2H3v2z"/></svg>
|
After Width: | Height: | Size: 185 B |
42
docs/flexsearch.min.js
vendored
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
FlexSearch v0.6.30
|
||||||
|
Copyright 2019 Nextapps GmbH
|
||||||
|
Author: Thomas Wilkerling
|
||||||
|
Released under the Apache 2.0 Licence
|
||||||
|
https://github.com/nextapps-de/flexsearch
|
||||||
|
*/
|
||||||
|
'use strict';(function(K,R,w){let L;(L=w.define)&&L.amd?L([],function(){return R}):(L=w.modules)?L[K.toLowerCase()]=R:"object"===typeof exports?module.exports=R:w[K]=R})("FlexSearch",function ma(K){function w(a,c){const b=c?c.id:a&&a.id;this.id=b||0===b?b:na++;this.init(a,c);fa(this,"index",function(){return this.a?Object.keys(this.a.index[this.a.keys[0]].c):Object.keys(this.c)});fa(this,"length",function(){return this.index.length})}function L(a,c,b,d){this.u!==this.g&&(this.o=this.o.concat(b),this.u++,
|
||||||
|
d&&this.o.length>=d&&(this.u=this.g),this.u===this.g&&(this.cache&&this.j.set(c,this.o),this.F&&this.F(this.o)));return this}function S(a){const c=B();for(const b in a)if(a.hasOwnProperty(b)){const d=a[b];F(d)?c[b]=d.slice(0):G(d)?c[b]=S(d):c[b]=d}return c}function W(a,c){const b=a.length,d=O(c),e=[];for(let f=0,h=0;f<b;f++){const g=a[f];if(d&&c(g)||!d&&!c[g])e[h++]=g}return e}function P(a,c,b,d,e,f,h,g,k,l){b=ha(b,h?0:e,g,f,c,k,l);let p;g&&(g=b.page,p=b.next,b=b.result);if(h)c=this.where(h,null,
|
||||||
|
e,b);else{c=b;b=this.l;e=c.length;f=Array(e);for(h=0;h<e;h++)f[h]=b[c[h]];c=f}b=c;d&&(O(d)||(M=d.split(":"),1<M.length?d=oa:(M=M[0],d=pa)),b.sort(d));b=T(g,p,b);this.cache&&this.j.set(a,b);return b}function fa(a,c,b){Object.defineProperty(a,c,{get:b})}function r(a){return new RegExp(a,"g")}function Q(a,c){for(let b=0;b<c.length;b+=2)a=a.replace(c[b],c[b+1]);return a}function V(a,c,b,d,e,f,h,g){if(c[b])return c[b];e=e?(g-(h||g/1.5))*f+(h||g/1.5)*e:f;c[b]=e;e>=h&&(a=a[g-(e+.5>>0)],a=a[b]||(a[b]=[]),
|
||||||
|
a[a.length]=d);return e}function ba(a,c){if(a){const b=Object.keys(a);for(let d=0,e=b.length;d<e;d++){const f=b[d],h=a[f];if(h)for(let g=0,k=h.length;g<k;g++)if(h[g]===c){1===k?delete a[f]:h.splice(g,1);break}else G(h[g])&&ba(h[g],c)}}}function ca(a){let c="",b="";var d="";for(let e=0;e<a.length;e++){const f=a[e];if(f!==b)if(e&&"h"===f){if(d="a"===d||"e"===d||"i"===d||"o"===d||"u"===d||"y"===d,("a"===b||"e"===b||"i"===b||"o"===b||"u"===b||"y"===b)&&d||" "===b)c+=f}else c+=f;d=e===a.length-1?"":a[e+
|
||||||
|
1];b=f}return c}function qa(a,c){a=a.length-c.length;return 0>a?1:a?-1:0}function pa(a,c){a=a[M];c=c[M];return a<c?-1:a>c?1:0}function oa(a,c){const b=M.length;for(let d=0;d<b;d++)a=a[M[d]],c=c[M[d]];return a<c?-1:a>c?1:0}function T(a,c,b){return a?{page:a,next:c?""+c:null,result:b}:b}function ha(a,c,b,d,e,f,h){let g,k=[];if(!0===b){b="0";var l=""}else l=b&&b.split(":");const p=a.length;if(1<p){const y=B(),t=[];let v,x;var n=0,m;let I;var u=!0;let D,E=0,N,da,X,ea;l&&(2===l.length?(X=l,l=!1):l=ea=
|
||||||
|
parseInt(l[0],10));if(h){for(v=B();n<p;n++)if("not"===e[n])for(x=a[n],I=x.length,m=0;m<I;m++)v["@"+x[m]]=1;else da=n+1;if(C(da))return T(b,g,k);n=0}else N=J(e)&&e;let Y;for(;n<p;n++){const ra=n===(da||p)-1;if(!N||!n)if((m=N||e&&e[n])&&"and"!==m)if("or"===m)Y=!1;else continue;else Y=f=!0;x=a[n];if(I=x.length){if(u)if(D){var q=D.length;for(m=0;m<q;m++){u=D[m];var A="@"+u;h&&v[A]||(y[A]=1,f||(k[E++]=u))}D=null;u=!1}else{D=x;continue}A=!1;for(m=0;m<I;m++){q=x[m];var z="@"+q;const Z=f?y[z]||0:n;if(!(!Z&&
|
||||||
|
!d||h&&v[z]||!f&&y[z]))if(Z===n){if(ra){if(!ea||--ea<E)if(k[E++]=q,c&&E===c)return T(b,E+(l||0),k)}else y[z]=n+1;A=!0}else d&&(z=t[Z]||(t[Z]=[]),z[z.length]=q)}if(Y&&!A&&!d)break}else if(Y&&!d)return T(b,g,x)}if(D)if(n=D.length,h)for(m=l?parseInt(l,10):0;m<n;m++)a=D[m],v["@"+a]||(k[E++]=a);else k=D;if(d)for(E=k.length,X?(n=parseInt(X[0],10)+1,m=parseInt(X[1],10)+1):(n=t.length,m=0);n--;)if(q=t[n]){for(I=q.length;m<I;m++)if(d=q[m],!h||!v["@"+d])if(k[E++]=d,c&&E===c)return T(b,n+":"+m,k);m=0}}else!p||
|
||||||
|
e&&"not"===e[0]||(k=a[0],l&&(l=parseInt(l[0],10)));c&&(h=k.length,l&&l>h&&(l=0),l=l||0,g=l+c,g<h?k=k.slice(l,g):(g=0,l&&(k=k.slice(l))));return T(b,g,k)}function J(a){return"string"===typeof a}function F(a){return a.constructor===Array}function O(a){return"function"===typeof a}function G(a){return"object"===typeof a}function C(a){return"undefined"===typeof a}function ia(a){const c=Array(a);for(let b=0;b<a;b++)c[b]=B();return c}function B(){return Object.create(null)}function sa(){let a,c;self.onmessage=
|
||||||
|
function(b){if(b=b.data)if(b.search){const d=c.search(b.content,b.threshold?{limit:b.limit,threshold:b.threshold,where:b.where}:b.limit);self.postMessage({id:a,content:b.content,limit:b.limit,result:d})}else b.add?c.add(b.id,b.content):b.update?c.update(b.id,b.content):b.remove?c.remove(b.id):b.clear?c.clear():b.info?(b=c.info(),b.worker=a,console.log(b)):b.register&&(a=b.id,b.options.cache=!1,b.options.async=!1,b.options.worker=!1,c=(new Function(b.register.substring(b.register.indexOf("{")+1,b.register.lastIndexOf("}"))))(),
|
||||||
|
c=new c(b.options))}}function ta(a,c,b,d){a=K("flexsearch","id"+a,sa,function(f){(f=f.data)&&f.result&&d(f.id,f.content,f.result,f.limit,f.where,f.cursor,f.suggest)},c);const e=ma.toString();b.id=c;a.postMessage({register:e,options:b,id:c});return a}const H={encode:"icase",f:"forward",split:/\W+/,cache:!1,async:!1,g:!1,D:!1,a:!1,b:9,threshold:0,depth:0},ja={memory:{encode:"extra",f:"strict",threshold:0,b:1},speed:{encode:"icase",f:"strict",threshold:1,b:3,depth:2},match:{encode:"extra",f:"full",threshold:1,
|
||||||
|
b:3},score:{encode:"extra",f:"strict",threshold:1,b:9,depth:4},balance:{encode:"balance",f:"strict",threshold:0,b:3,depth:3},fast:{encode:"icase",f:"strict",threshold:8,b:9,depth:1}},aa=[];let na=0;const ka={},la={};w.create=function(a,c){return new w(a,c)};w.registerMatcher=function(a){for(const c in a)a.hasOwnProperty(c)&&aa.push(r(c),a[c]);return this};w.registerEncoder=function(a,c){U[a]=c.bind(U);return this};w.registerLanguage=function(a,c){ka[a]=c.filter;la[a]=c.stemmer;return this};w.encode=
|
||||||
|
function(a,c){return U[a](c)};w.prototype.init=function(a,c){this.v=[];if(c){var b=c.preset;a=c}else a||(a=H),b=a.preset;c={};J(a)?(c=ja[a],a={}):b&&(c=ja[b]);if(b=a.worker)if("undefined"===typeof Worker)a.worker=!1,this.m=null;else{var d=parseInt(b,10)||4;this.C=-1;this.u=0;this.o=[];this.F=null;this.m=Array(d);for(var e=0;e<d;e++)this.m[e]=ta(this.id,e,a,L.bind(this))}this.f=a.tokenize||c.f||this.f||H.f;this.split=C(b=a.split)?this.split||H.split:J(b)?r(b):b;this.D=a.rtl||this.D||H.D;this.async=
|
||||||
|
"undefined"===typeof Promise||C(b=a.async)?this.async||H.async:b;this.g=C(b=a.worker)?this.g||H.g:b;this.threshold=C(b=a.threshold)?c.threshold||this.threshold||H.threshold:b;this.b=C(b=a.resolution)?b=c.b||this.b||H.b:b;b<=this.threshold&&(this.b=this.threshold+1);this.depth="strict"!==this.f||C(b=a.depth)?c.depth||this.depth||H.depth:b;this.w=(b=C(b=a.encode)?c.encode||H.encode:b)&&U[b]&&U[b].bind(U)||(O(b)?b:this.w||!1);(b=a.matcher)&&this.addMatcher(b);if(b=(c=a.lang)||a.filter){J(b)&&(b=ka[b]);
|
||||||
|
if(F(b)){d=this.w;e=B();for(var f=0;f<b.length;f++){var h=d?d(b[f]):b[f];e[h]=1}b=e}this.filter=b}if(b=c||a.stemmer){var g;c=J(b)?la[b]:b;d=this.w;e=[];for(g in c)c.hasOwnProperty(g)&&(f=d?d(g):g,e.push(r(f+"($|\\W)"),d?d(c[g]):c[g]));this.stemmer=g=e}this.a=e=(b=a.doc)?S(b):this.a||H.a;this.i=ia(this.b-(this.threshold||0));this.h=B();this.c=B();if(e){this.l=B();a.doc=null;g=e.index={};c=e.keys=[];d=e.field;f=e.tag;h=e.store;F(e.id)||(e.id=e.id.split(":"));if(h){var k=B();if(J(h))k[h]=1;else if(F(h))for(let l=
|
||||||
|
0;l<h.length;l++)k[h[l]]=1;else G(h)&&(k=h);e.store=k}if(f){this.G=B();h=B();if(d)if(J(d))h[d]=a;else if(F(d))for(k=0;k<d.length;k++)h[d[k]]=a;else G(d)&&(h=d);F(f)||(e.tag=f=[f]);for(d=0;d<f.length;d++)this.G[f[d]]=B();this.I=f;d=h}if(d){let l;F(d)||(G(d)?(l=d,e.field=d=Object.keys(d)):e.field=d=[d]);for(e=0;e<d.length;e++)f=d[e],F(f)||(l&&(a=l[f]),c[e]=f,d[e]=f.split(":")),g[f]=new w(a)}a.doc=b}this.B=!0;this.j=(this.cache=b=C(b=a.cache)?this.cache||H.cache:b)?new ua(b):!1;return this};w.prototype.encode=
|
||||||
|
function(a){a&&(aa.length&&(a=Q(a,aa)),this.v.length&&(a=Q(a,this.v)),this.w&&(a=this.w(a)),this.stemmer&&(a=Q(a,this.stemmer)));return a};w.prototype.addMatcher=function(a){const c=this.v;for(const b in a)a.hasOwnProperty(b)&&c.push(r(b),a[b]);return this};w.prototype.add=function(a,c,b,d,e){if(this.a&&G(a))return this.A("add",a,c);if(c&&J(c)&&(a||0===a)){var f="@"+a;if(this.c[f]&&!d)return this.update(a,c);if(this.g)return++this.C>=this.m.length&&(this.C=0),this.m[this.C].postMessage({add:!0,id:a,
|
||||||
|
content:c}),this.c[f]=""+this.C,b&&b(),this;if(!e){if(this.async&&"function"!==typeof importScripts){let t=this;f=new Promise(function(v){setTimeout(function(){t.add(a,c,null,d,!0);t=null;v()})});if(b)f.then(b);else return f;return this}if(b)return this.add(a,c,null,d,!0),b(),this}c=this.encode(c);if(!c.length)return this;b=this.f;e=O(b)?b(c):c.split(this.split);this.filter&&(e=W(e,this.filter));const n=B();n._ctx=B();const m=e.length,u=this.threshold,q=this.depth,A=this.b,z=this.i,y=this.D;for(let t=
|
||||||
|
0;t<m;t++){var h=e[t];if(h){var g=h.length,k=(y?t+1:m-t)/m,l="";switch(b){case "reverse":case "both":for(var p=g;--p;)l=h[p]+l,V(z,n,l,a,y?1:(g-p)/g,k,u,A-1);l="";case "forward":for(p=0;p<g;p++)l+=h[p],V(z,n,l,a,y?(p+1)/g:1,k,u,A-1);break;case "full":for(p=0;p<g;p++){const v=(y?p+1:g-p)/g;for(let x=g;x>p;x--)l=h.substring(p,x),V(z,n,l,a,v,k,u,A-1)}break;default:if(g=V(z,n,h,a,1,k,u,A-1),q&&1<m&&g>=u)for(g=n._ctx[h]||(n._ctx[h]=B()),h=this.h[h]||(this.h[h]=ia(A-(u||0))),k=t-q,l=t+q+1,0>k&&(k=0),l>
|
||||||
|
m&&(l=m);k<l;k++)k!==t&&V(h,g,e[k],a,0,A-(k<t?t-k:k-t),u,A-1)}}}this.c[f]=1;this.B=!1}return this};w.prototype.A=function(a,c,b){if(F(c)){var d=c.length;if(d--){for(var e=0;e<d;e++)this.A(a,c[e]);return this.A(a,c[d],b)}}else{var f=this.a.index,h=this.a.keys,g=this.a.tag;e=this.a.store;var k;var l=this.a.id;d=c;for(var p=0;p<l.length;p++)d=d[l[p]];if("remove"===a&&(delete this.l[d],l=h.length,l--)){for(c=0;c<l;c++)f[h[c]].remove(d);return f[h[l]].remove(d,b)}if(g){for(k=0;k<g.length;k++){var n=g[k];
|
||||||
|
var m=c;l=n.split(":");for(p=0;p<l.length;p++)m=m[l[p]];m="@"+m}k=this.G[n];k=k[m]||(k[m]=[])}l=this.a.field;for(let u=0,q=l.length;u<q;u++){n=l[u];g=c;for(m=0;m<n.length;m++)g=g[n[m]];n=f[h[u]];m="add"===a?n.add:n.update;u===q-1?m.call(n,d,g,b):m.call(n,d,g)}if(e){b=Object.keys(e);a=B();for(f=0;f<b.length;f++)if(h=b[f],e[h]){h=h.split(":");let u,q;for(l=0;l<h.length;l++)g=h[l],u=(u||c)[g],q=(q||a)[g]=u}c=a}k&&(k[k.length]=c);this.l[d]=c}return this};w.prototype.update=function(a,c,b){if(this.a&&
|
||||||
|
G(a))return this.A("update",a,c);this.c["@"+a]&&J(c)&&(this.remove(a),this.add(a,c,b,!0));return this};w.prototype.remove=function(a,c,b){if(this.a&&G(a))return this.A("remove",a,c);var d="@"+a;if(this.c[d]){if(this.g)return this.m[this.c[d]].postMessage({remove:!0,id:a}),delete this.c[d],c&&c(),this;if(!b){if(this.async&&"function"!==typeof importScripts){let e=this;d=new Promise(function(f){setTimeout(function(){e.remove(a,null,!0);e=null;f()})});if(c)d.then(c);else return d;return this}if(c)return this.remove(a,
|
||||||
|
null,!0),c(),this}for(c=0;c<this.b-(this.threshold||0);c++)ba(this.i[c],a);this.depth&&ba(this.h,a);delete this.c[d];this.B=!1}return this};let M;w.prototype.search=function(a,c,b,d){if(G(c)){if(F(c))for(var e=0;e<c.length;e++)c[e].query=a;else c.query=a;a=c;c=1E3}else c&&O(c)?(b=c,c=1E3):c||0===c||(c=1E3);if(this.g){this.F=b;this.u=0;this.o=[];for(var f=0;f<this.g;f++)this.m[f].postMessage({search:!0,limit:c,content:a})}else{var h=[],g=a;if(G(a)&&!F(a)){b||(b=a.callback)&&(g.callback=null);var k=
|
||||||
|
a.sort;var l=a.page;c=a.limit;f=a.threshold;var p=a.suggest;a=a.query}if(this.a){f=this.a.index;const y=g.where;var n=g.bool||"or",m=g.field;let t=n;let v,x;if(m)F(m)||(m=[m]);else if(F(g)){var u=g;m=[];t=[];for(var q=0;q<g.length;q++)d=g[q],e=d.bool||n,m[q]=d.field,t[q]=e,"not"===e?v=!0:"and"===e&&(x=!0)}else m=this.a.keys;n=m.length;for(q=0;q<n;q++)u&&(g=u[q]),l&&!J(g)&&(g.page=null,g.limit=0),h[q]=f[m[q]].search(g,0);if(b)return b(P.call(this,a,t,h,k,c,p,y,l,x,v));if(this.async){const I=this;return new Promise(function(D){Promise.all(h).then(function(E){D(P.call(I,
|
||||||
|
a,t,E,k,c,p,y,l,x,v))})})}return P.call(this,a,t,h,k,c,p,y,l,x,v)}f||(f=this.threshold||0);if(!d){if(this.async&&"function"!==typeof importScripts){let y=this;f=new Promise(function(t){setTimeout(function(){t(y.search(g,c,null,!0));y=null})});if(b)f.then(b);else return f;return this}if(b)return b(this.search(g,c,null,!0)),this}if(!a||!J(a))return h;g=a;if(this.cache)if(this.B){if(b=this.j.get(a))return b}else this.j.clear(),this.B=!0;g=this.encode(g);if(!g.length)return h;b=this.f;b=O(b)?b(g):g.split(this.split);
|
||||||
|
this.filter&&(b=W(b,this.filter));u=b.length;d=!0;e=[];var A=B(),z=0;1<u&&(this.depth&&"strict"===this.f?n=!0:b.sort(qa));if(!n||(q=this.h)){const y=this.b;for(;z<u;z++){let t=b[z];if(t){if(n){if(!m)if(q[t])m=t,A[t]=1;else if(!p)return h;if(p&&z===u-1&&!e.length)n=!1,t=m||t,A[t]=0;else if(!m)continue}if(!A[t]){const v=[];let x=!1,I=0;const D=n?q[m]:this.i;if(D){let E;for(let N=0;N<y-f;N++)if(E=D[N]&&D[N][t])v[I++]=E,x=!0}if(x)m=t,e[e.length]=1<I?v.concat.apply([],v):v[0];else if(!p){d=!1;break}A[t]=
|
||||||
|
1}}}}else d=!1;d&&(h=ha(e,c,l,p));this.cache&&this.j.set(a,h);return h}};w.prototype.find=function(a,c){return this.where(a,c,1)[0]||null};w.prototype.where=function(a,c,b,d){const e=this.l,f=[];let h=0;let g;var k;let l;if(G(a)){b||(b=c);var p=Object.keys(a);var n=p.length;g=!1;if(1===n&&"id"===p[0])return[e[a.id]];if((k=this.I)&&!d)for(var m=0;m<k.length;m++){var u=k[m],q=a[u];if(!C(q)){l=this.G[u]["@"+q];if(0===--n)return l;p.splice(p.indexOf(u),1);delete a[u];break}}k=Array(n);for(m=0;m<n;m++)k[m]=
|
||||||
|
p[m].split(":")}else{if(O(a)){c=d||Object.keys(e);b=c.length;for(p=0;p<b;p++)n=e[c[p]],a(n)&&(f[h++]=n);return f}if(C(c))return[e[a]];if("id"===a)return[e[c]];p=[a];n=1;k=[a.split(":")];g=!0}d=l||d||Object.keys(e);m=d.length;for(u=0;u<m;u++){q=l?d[u]:e[d[u]];let A=!0;for(let z=0;z<n;z++){g||(c=a[p[z]]);const y=k[z],t=y.length;let v=q;if(1<t)for(let x=0;x<t;x++)v=v[y[x]];else v=v[y[0]];if(v!==c){A=!1;break}}if(A&&(f[h++]=q,b&&h===b))break}return f};w.prototype.info=function(){if(this.g)for(let a=0;a<
|
||||||
|
this.g;a++)this.m[a].postMessage({info:!0,id:this.id});else return{id:this.id,items:this.length,cache:this.cache&&this.cache.s?this.cache.s.length:!1,matcher:aa.length+(this.v?this.v.length:0),worker:this.g,threshold:this.threshold,depth:this.depth,resolution:this.b,contextual:this.depth&&"strict"===this.f}};w.prototype.clear=function(){return this.destroy().init()};w.prototype.destroy=function(){this.cache&&(this.j.clear(),this.j=null);this.i=this.h=this.c=null;if(this.a){const a=this.a.keys;for(let c=
|
||||||
|
0;c<a.length;c++)this.a.index[a[c]].destroy();this.a=this.l=null}return this};w.prototype.export=function(a){const c=!a||C(a.serialize)||a.serialize;if(this.a){const d=!a||C(a.doc)||a.doc;var b=!a||C(a.index)||a.index;a=[];let e=0;if(b)for(b=this.a.keys;e<b.length;e++){const f=this.a.index[b[e]];a[e]=[f.i,f.h,Object.keys(f.c)]}d&&(a[e]=this.l)}else a=[this.i,this.h,Object.keys(this.c)];c&&(a=JSON.stringify(a));return a};w.prototype.import=function(a,c){if(!c||C(c.serialize)||c.serialize)a=JSON.parse(a);
|
||||||
|
const b=B();if(this.a){var d=!c||C(c.doc)||c.doc,e=0;if(!c||C(c.index)||c.index){c=this.a.keys;const h=c.length;for(var f=a[0][2];e<f.length;e++)b[f[e]]=1;for(e=0;e<h;e++){f=this.a.index[c[e]];const g=a[e];g&&(f.i=g[0],f.h=g[1],f.c=b)}}d&&(this.l=G(d)?d:a[e])}else{d=a[2];for(e=0;e<d.length;e++)b[d[e]]=1;this.i=a[0];this.h=a[1];this.c=b}};const va=function(){const a=r("\\s+"),c=r("[^a-z0-9 ]"),b=[r("[-/]")," ",c,"",a," "];return function(d){return ca(Q(d.toLowerCase(),b))}}(),U={icase:function(a){return a.toLowerCase()},
|
||||||
|
simple:function(){const a=r("\\s+"),c=r("[^a-z0-9 ]"),b=r("[-/]"),d=r("[\u00e0\u00e1\u00e2\u00e3\u00e4\u00e5]"),e=r("[\u00e8\u00e9\u00ea\u00eb]"),f=r("[\u00ec\u00ed\u00ee\u00ef]"),h=r("[\u00f2\u00f3\u00f4\u00f5\u00f6\u0151]"),g=r("[\u00f9\u00fa\u00fb\u00fc\u0171]"),k=r("[\u00fd\u0177\u00ff]"),l=r("\u00f1"),p=r("[\u00e7c]"),n=r("\u00df"),m=r(" & "),u=[d,"a",e,"e",f,"i",h,"o",g,"u",k,"y",l,"n",p,"k",n,"s",m," and ",b," ",c,"",a," "];return function(q){q=Q(q.toLowerCase(),u);return" "===q?"":q}}(),advanced:function(){const a=
|
||||||
|
r("ae"),c=r("ai"),b=r("ay"),d=r("ey"),e=r("oe"),f=r("ue"),h=r("ie"),g=r("sz"),k=r("zs"),l=r("ck"),p=r("cc"),n=r("sh"),m=r("th"),u=r("dt"),q=r("ph"),A=r("pf"),z=r("ou"),y=r("uo"),t=[a,"a",c,"ei",b,"ei",d,"ei",e,"o",f,"u",h,"i",g,"s",k,"s",n,"s",l,"k",p,"k",m,"t",u,"t",q,"f",A,"f",z,"o",y,"u"];return function(v,x){if(!v)return v;v=this.simple(v);2<v.length&&(v=Q(v,t));x||1<v.length&&(v=ca(v));return v}}(),extra:function(){const a=r("p"),c=r("z"),b=r("[cgq]"),d=r("n"),e=r("d"),f=r("[vw]"),h=r("[aeiouy]"),
|
||||||
|
g=[a,"b",c,"s",b,"k",d,"m",e,"t",f,"f",h,""];return function(k){if(!k)return k;k=this.advanced(k,!0);if(1<k.length){k=k.split(" ");for(let l=0;l<k.length;l++){const p=k[l];1<p.length&&(k[l]=p[0]+Q(p.substring(1),g))}k=k.join(" ");k=ca(k)}return k}}(),balance:va},ua=function(){function a(c){this.clear();this.H=!0!==c&&c}a.prototype.clear=function(){this.cache=B();this.count=B();this.index=B();this.s=[]};a.prototype.set=function(c,b){if(this.H&&C(this.cache[c])){let d=this.s.length;if(d===this.H){d--;
|
||||||
|
const e=this.s[d];delete this.cache[e];delete this.count[e];delete this.index[e]}this.index[c]=d;this.s[d]=c;this.count[c]=-1;this.cache[c]=b;this.get(c)}else this.cache[c]=b};a.prototype.get=function(c){const b=this.cache[c];if(this.H&&b){var d=++this.count[c];const f=this.index;let h=f[c];if(0<h){const g=this.s;for(var e=h;this.count[g[--h]]<=d&&-1!==h;);h++;if(h!==e){for(d=e;d>h;d--)e=g[d-1],g[d]=e,f[e]=d;g[h]=c;f[c]=h}}}return b};return a}();return w}(function(){const K={},R="undefined"!==typeof Blob&&
|
||||||
|
"undefined"!==typeof URL&&URL.createObjectURL;return function(w,L,S,W,P){S=R?URL.createObjectURL(new Blob(["("+S.toString()+")()"],{type:"text/javascript"})):w+".min.js";w+="-"+L;K[w]||(K[w]=[]);K[w][P]=new Worker(S);K[w][P].onmessage=W;return K[w][P]}}()),this);
|
BIN
docs/fonts/roboto-mono-v13-latin-regular.woff
Normal file
BIN
docs/fonts/roboto-mono-v13-latin-regular.woff2
Normal file
BIN
docs/fonts/roboto-v27-latin-700.woff
Normal file
BIN
docs/fonts/roboto-v27-latin-700.woff2
Normal file
BIN
docs/fonts/roboto-v27-latin-regular.woff
Normal file
BIN
docs/fonts/roboto-v27-latin-regular.woff2
Normal file
112
docs/index.html
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
<!doctype html><html lang=en-us dir=ltr>
|
||||||
|
<head>
|
||||||
|
<meta name=generator content="Hugo 0.92.2">
|
||||||
|
<meta charset=utf-8>
|
||||||
|
<meta name=viewport content="width=device-width,initial-scale=1">
|
||||||
|
<meta name=description content="Introduction # WiTTY is a cross-platform, open-source, web-based terminal emulator. It exports the terminal interface on the server (i.e., where WiTTY runs) to the browser. Simply run WiTTY on a computer and give it the command to execute when users connect via the browser. WiTTY has the following features that distinguish itself from other similar tools:
|
||||||
|
WiTTY allows users to easily record, replay, and share console sessions with just a few clicks.">
|
||||||
|
<meta name=theme-color content="#FFFFFF">
|
||||||
|
<meta name=color-scheme content="light dark"><meta property="og:title" content="Introduction">
|
||||||
|
<meta property="og:description" content>
|
||||||
|
<meta property="og:type" content="website">
|
||||||
|
<meta property="og:url" content="https://syssecfsu.github.io/witty/">
|
||||||
|
<title>Introduction | WiTTY</title>
|
||||||
|
<link rel=manifest href=https://syssecfsu.github.io/witty/manifest.json>
|
||||||
|
<link rel=icon href=https://syssecfsu.github.io/witty/favicon.png type=image/x-icon>
|
||||||
|
<link rel=stylesheet href=https://syssecfsu.github.io/witty/book.min.ce78b1b74b3a1081ebd0d36b0eb48d28d6d6f79be1ed3d41925fcc9a239d7a44.css integrity="sha256-znixt0s6EIHr0NNrDrSNKNbW95vh7T1Bkl/MmiOdekQ=" crossorigin=anonymous>
|
||||||
|
<script defer src=https://syssecfsu.github.io/witty/flexsearch.min.js></script>
|
||||||
|
<script defer src=https://syssecfsu.github.io/witty/en.search.min.c2e16a8098729d8520f5f13d7a4e43262b74a3ca3c2e8681edd14da9dc145e7e.js integrity="sha256-wuFqgJhynYUg9fE9ek5DJit0o8o8LoaB7dFNqdwUXn4=" crossorigin=anonymous></script>
|
||||||
|
<link rel=alternate type=application/rss+xml href=https://syssecfsu.github.io/witty/index.xml title=WiTTY>
|
||||||
|
</head>
|
||||||
|
<body dir=ltr>
|
||||||
|
<input type=checkbox class="hidden toggle" id=menu-control>
|
||||||
|
<input type=checkbox class="hidden toggle" id=toc-control>
|
||||||
|
<main class="container flex">
|
||||||
|
<aside class=book-menu>
|
||||||
|
<div class=book-menu-content>
|
||||||
|
<nav>
|
||||||
|
<h2 class=book-brand>
|
||||||
|
<a class="flex align-center" href=https://syssecfsu.github.io/witty/><img src=https://syssecfsu.github.io/witty/static/img/logo.svg alt=Logo><span>WiTTY</span>
|
||||||
|
</a>
|
||||||
|
</h2>
|
||||||
|
<div class=book-search>
|
||||||
|
<input type=text id=book-search-input placeholder=Search aria-label=Search maxlength=64 data-hotkeys=s/>
|
||||||
|
<div class="book-search-spinner hidden"></div>
|
||||||
|
<ul id=book-search-results></ul>
|
||||||
|
</div>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<a href=https://syssecfsu.github.io/witty/docs/install/>Installation</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href=https://syssecfsu.github.io/witty/docs/ui/>User Interface</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href=https://syssecfsu.github.io/witty/docs/record/>Record Sessions</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href=https://syssecfsu.github.io/witty/docs/vm/>VirtualBox</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href=https://syssecfsu.github.io/witty/docs/about/>About</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
<script>(function(){var a=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(b){localStorage.setItem("menu.scrollTop",a.scrollTop)}),a.scrollTop=localStorage.getItem("menu.scrollTop")})()</script>
|
||||||
|
</div>
|
||||||
|
</aside>
|
||||||
|
<div class=book-page>
|
||||||
|
<header class=book-header>
|
||||||
|
<div class="flex align-center justify-between">
|
||||||
|
<label for=menu-control>
|
||||||
|
<img src=https://syssecfsu.github.io/witty/svg/menu.svg class=book-icon alt=Menu>
|
||||||
|
</label>
|
||||||
|
<strong>Introduction</strong>
|
||||||
|
<label for=toc-control>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
<article class=markdown><h1 id=introduction>
|
||||||
|
Introduction
|
||||||
|
<a class=anchor href=#introduction>#</a>
|
||||||
|
</h1>
|
||||||
|
<p><a href=https://github.com/syssecfsu/witty>WiTTY</a> is a cross-platform, open-source, web-based terminal emulator. It exports the terminal interface on the server (i.e., where WiTTY runs) to the browser. Simply run WiTTY on a computer and give it the command to execute when users connect via the browser. WiTTY has the following features that distinguish itself from other similar tools:</p>
|
||||||
|
<ol>
|
||||||
|
<li>
|
||||||
|
<p>WiTTY allows users to <strong>easily record, replay, and share console sessions</strong> with just a few clicks.</p>
|
||||||
|
<blockquote class="book-hint info">
|
||||||
|
WiTTY was designed to help teach courses that use UNIX-like command-line interface, such as the software and system security parts of <a href=https://seedsecuritylabs.org/Labs_20.04/>the SEED labs</a>. The biggest challenge in teaching a large such course in the era of pandemic is how to answer students' questions <strong>in the first try</strong>, avoiding long exchange of emails and screenshots. WiTTY makes it a breeze to answer such questions. It is much easier to spot problems with the full history of console sessions.
|
||||||
|
</blockquote>
|
||||||
|
<blockquote class="book-hint info">
|
||||||
|
Instructors can also record their demos in WiTTY and send them to students. Students can replay these demons however they want. This would help them better understand the knowledge.
|
||||||
|
</blockquote>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>It allows others to <strong>view ongoing interactive sessions</strong>. This is useful for providing live remote help.</p>
|
||||||
|
<blockquote class="book-hint info">
|
||||||
|
<p>An envisioned use of this feature is to allow students to view live sessions of demos by the instructors. It can also allow live, remote help to students.</p>
|
||||||
|
<p>A challenge of this use case is that our home networks are almost always behind NAT, making it difficult to run WiTTY as a publicly accessible server. Security is also potentially a concern.</p>
|
||||||
|
</blockquote>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>Great attention has been paid to ensure the cleanses of the code. This, hopefully, provides a useful counter-example of <strong>Do as I say, but not as I do</strong>.</p>
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
</article>
|
||||||
|
<footer class=book-footer>
|
||||||
|
<div class="flex flex-wrap justify-between">
|
||||||
|
<div><a class="flex align-center" href=https://github.com/syssecfsu/witty-docs/commit/924a38a5d2aae36483f97d8669c89b57a01031de title="Last modified by Zhi Wang | February 7, 2022" target=_blank rel=noopener>
|
||||||
|
<img src=https://syssecfsu.github.io/witty/svg/calendar.svg class=book-icon alt=Calendar>
|
||||||
|
<span>February 7, 2022</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script>(function(){function a(c){const a=window.getSelection(),b=document.createRange();b.selectNodeContents(c),a.removeAllRanges(),a.addRange(b)}document.querySelectorAll("pre code").forEach(b=>{b.addEventListener("click",function(c){a(b.parentElement),navigator.clipboard&&navigator.clipboard.writeText(b.parentElement.textContent)})})})()</script>
|
||||||
|
</footer>
|
||||||
|
<div class=book-comments>
|
||||||
|
</div>
|
||||||
|
<label for=menu-control class="hidden book-menu-overlay"></label>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
12
docs/index.xml
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Introduction on WiTTY</title><link>https://syssecfsu.github.io/witty/</link><description>Recent content in Introduction on WiTTY</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><atom:link href="https://syssecfsu.github.io/witty/index.xml" rel="self" type="application/rss+xml"/><item><title>Installation</title><link>https://syssecfsu.github.io/witty/docs/install/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://syssecfsu.github.io/witty/docs/install/</guid><description>Installation # WiTTY runs on Linux (ARM and x86), macOS, and WSL2 (Windows subsystem for Linux, basically Linux). You can install from the pre-built binary or from the source code.
|
||||||
|
From Binary Visit the release page of WiTTY at https://github.com/syssecfsu/witty/releases
|
||||||
|
Download the release for your system
|
||||||
|
Decompress the binary with the following command at selected location.
|
||||||
|
tar -xzvf witty_vx.x.x_xxx.tar.gz
|
||||||
|
For example, use tar -xzvf witty_v1.</description></item><item><title>User Interface</title><link>https://syssecfsu.github.io/witty/docs/ui/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://syssecfsu.github.io/witty/docs/ui/</guid><description>Sub-commands # Similar to git and apt, WiTTY uses sub-commands for its various functions. WiTTY currently supports the following sub-commands: adduser, deluser, listusers, replay, merge, run.
|
||||||
|
Sub-command Description adduser Add/update an authenticated user with their password deluser Delete an authenticated user listusers List all the authenticated users replay Replay a recorded session (set your terminal to 120x36 first) merge Merge several recorded sessions into one session run Run a specified CLI program when user connects with browser Some sub-commands have options.</description></item><item><title>Record Sessions</title><link>https://syssecfsu.github.io/witty/docs/record/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://syssecfsu.github.io/witty/docs/record/</guid><description>Record Sessions # You can record an ongoing session in the interactive terminal window.
|
||||||
|
Recorded sessions will be listed in the main window of WiTTY. You can click the button to rename a recorded session. By default, a recorded session is named based on its session ID and the current time, not very meaningful for human. Rename them to something easy to remember, such as task1, task2,&hellip;
|
||||||
|
WiTTY provides two sub-commands to merge and replay recorded sessions.</description></item><item><title>VirtualBox</title><link>https://syssecfsu.github.io/witty/docs/vm/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://syssecfsu.github.io/witty/docs/vm/</guid><description>Use WiTTY with SEED VM # The SEED labs provides a number of security hands-on labs. It is a popular security lab course taught at many universities. The SEED labs use VirtualBox to run its VMs (because VirtualBox is cross-platform.)
|
||||||
|
By default, the SEED VM uses only NAT-based network, which means that the VM can access the Internet but not the host machine (i.e., the machine that runs VirtualBox is called the host, and the VM is often called the guest.</description></item><item><title>About</title><link>https://syssecfsu.github.io/witty/docs/about/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://syssecfsu.github.io/witty/docs/about/</guid><description>About WiTTY # WiTTY is written in the go programming language, using the Gin web framework, gorilla/websocket, pty, and the wonderful xterm.js! The workflow is simple, the client initiates a terminal window (xterm.js) and creates a websocket with the server, which relays the data between pty and xterm.
|
||||||
|
The program has been tested on Linux, WSL2, Raspberry Pi 3B (Debian), and MacOSX using Google Chrome, Firefox, and Safari.
|
||||||
|
Most icons were provided by fontawesome under this license.</description></item><item><title/><link>https://syssecfsu.github.io/witty/menu/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://syssecfsu.github.io/witty/menu/</guid><description> Installation User Interface Record Sessions VirtualBox About</description></item></channel></rss>
|
1
docs/katex/auto-render.min.js
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t(require("katex")):"function"==typeof define&&define.amd?define(["katex"],t):"object"==typeof exports?exports.renderMathInElement=t(require("katex")):e.renderMathInElement=t(e.katex)}("undefined"!=typeof self?self:this,function(e){return function(e){var t={};function r(n){if(t[n])return t[n].exports;var o=t[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}return r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)r.d(n,o,function(t){return e[t]}.bind(null,o));return n},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="",r(r.s=1)}([function(t,r){t.exports=e},function(e,t,r){"use strict";r.r(t);var n=r(0),o=r.n(n),a=function(e,t,r){for(var n=r,o=0,a=e.length;n<t.length;){var i=t[n];if(o<=0&&t.slice(n,n+a)===e)return n;"\\"===i?n++:"{"===i?o++:"}"===i&&o--,n++}return-1},i=function(e,t,r,n){for(var o=[],i=0;i<e.length;i++)if("text"===e[i].type){var l=e[i].data,d=!0,s=0,f=void 0;for(-1!==(f=l.indexOf(t))&&(s=f,o.push({type:"text",data:l.slice(0,s)}),d=!1);;){if(d){if(-1===(f=l.indexOf(t,s)))break;o.push({type:"text",data:l.slice(s,f)}),s=f}else{if(-1===(f=a(r,l,s+t.length)))break;o.push({type:"math",data:l.slice(s+t.length,f),rawData:l.slice(s,f+r.length),display:n}),s=f+r.length}d=!d}o.push({type:"text",data:l.slice(s)})}else o.push(e[i]);return o},l=function(e,t){for(var r=function(e,t){for(var r=[{type:"text",data:e}],n=0;n<t.length;n++){var o=t[n];r=i(r,o.left,o.right,o.display||!1)}return r}(e,t.delimiters),n=document.createDocumentFragment(),a=0;a<r.length;a++)if("text"===r[a].type)n.appendChild(document.createTextNode(r[a].data));else{var l=document.createElement("span"),d=r[a].data;t.displayMode=r[a].display;try{t.preProcess&&(d=t.preProcess(d)),o.a.render(d,l,t)}catch(e){if(!(e instanceof o.a.ParseError))throw e;t.errorCallback("KaTeX auto-render: Failed to parse `"+r[a].data+"` with ",e),n.appendChild(document.createTextNode(r[a].rawData));continue}n.appendChild(l)}return n};t.default=function(e,t){if(!e)throw new Error("No element provided to render");var r={};for(var n in t)t.hasOwnProperty(n)&&(r[n]=t[n]);r.delimiters=r.delimiters||[{left:"$$",right:"$$",display:!0},{left:"\\(",right:"\\)",display:!1},{left:"\\[",right:"\\]",display:!0}],r.ignoredTags=r.ignoredTags||["script","noscript","style","textarea","pre","code"],r.ignoredClasses=r.ignoredClasses||[],r.errorCallback=r.errorCallback||console.error,r.macros=r.macros||{},function e(t,r){for(var n=0;n<t.childNodes.length;n++){var o=t.childNodes[n];if(3===o.nodeType){var a=l(o.textContent,r);n+=a.childNodes.length-1,t.replaceChild(a,o)}else 1===o.nodeType&&function(){var t=" "+o.className+" ";-1===r.ignoredTags.indexOf(o.nodeName.toLowerCase())&&r.ignoredClasses.every(function(e){return-1===t.indexOf(" "+e+" ")})&&e(o,r)}()}}(e,r)}}]).default});
|