aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShulhan <ms@kilabit.info>2023-12-03 00:56:53 +0700
committerShulhan <ms@kilabit.info>2023-12-07 04:11:46 +0700
commit5e38138e816293f2b305095be84191b162add1c5 (patch)
tree8861013538cc9d7b5d35daee0207178dfa0e5e04
parentd214fd59d1e130642ee266e5cde144d88eea7d3d (diff)
downloadawwan-5e38138e816293f2b305095be84191b162add1c5.tar.xz
_play: populate the content for play.awwan.org
-rw-r--r--_play/.awwan.env2
-rw-r--r--_play/.awwan.env.vaultbin0 -> 384 bytes
-rw-r--r--_play/.gitignore7
-rw-r--r--_play/.ssh/config8
-rw-r--r--_play/.vimrc3
-rw-r--r--_play/00_README.txt68
-rw-r--r--_play/01_local.aww50
-rw-r--r--_play/02_script_variables.aww28
-rw-r--r--_play/03_env.aww57
-rw-r--r--_play/04_env-set.aww41
-rw-r--r--_play/05_env-get.aww39
-rw-r--r--_play/06_magic_put.aww50
-rw-r--r--_play/07_magic_get.aww21
-rw-r--r--_play/08_encrypt.aww31
-rw-r--r--_play/09_decrypt.aww22
-rw-r--r--_play/10_encrypted_env.aww16
-rw-r--r--_play/11_encrypted_put.aww37
-rw-r--r--_play/12_magic_require.aww29
-rw-r--r--_play/app.conf.vaultbin0 -> 384 bytes
-rw-r--r--_play/awwan.env8
-rw-r--r--_play/put_source.txt1
-rw-r--r--_play/remotehost/01_play.aww53
-rw-r--r--_play/remotehost/02_magic_local.aww25
-rw-r--r--_play/remotehost/awwan.env2
-rw-r--r--_play/secret.txt1
25 files changed, 595 insertions, 4 deletions
diff --git a/_play/.awwan.env b/_play/.awwan.env
new file mode 100644
index 0000000..66b8f20
--- /dev/null
+++ b/_play/.awwan.env
@@ -0,0 +1,2 @@
+[user "awwan"]
+pass = s3cret
diff --git a/_play/.awwan.env.vault b/_play/.awwan.env.vault
new file mode 100644
index 0000000..15fb80b
--- /dev/null
+++ b/_play/.awwan.env.vault
Binary files differ
diff --git a/_play/.gitignore b/_play/.gitignore
new file mode 100644
index 0000000..12f8d5f
--- /dev/null
+++ b/_play/.gitignore
@@ -0,0 +1,7 @@
+/.cache
+/app.conf
+/get_shadow.txt
+/remotehost/app.conf
+/remotehost/put_secret.txt
+/remotehost/put_source.txt
+/secret.txt.vault
diff --git a/_play/.ssh/config b/_play/.ssh/config
index 983eee4..fba1de1 100644
--- a/_play/.ssh/config
+++ b/_play/.ssh/config
@@ -1,6 +1,6 @@
## This is an example of remote host to execute awwan command using "play".
Host remotehost
- Hostname 127.0.0.1
- Port 20022
- User awwanssh
- IdentityFile ~/.ssh/id_ed25519
+ Hostname 127.0.0.1
+ Port 20022
+ User awwanssh
+ IdentityFile ~/.ssh/id_ed25519
diff --git a/_play/.vimrc b/_play/.vimrc
new file mode 100644
index 0000000..0b527fd
--- /dev/null
+++ b/_play/.vimrc
@@ -0,0 +1,3 @@
+set expandtab
+set tabstop=2
+set shiftwidth=2
diff --git a/_play/00_README.txt b/_play/00_README.txt
new file mode 100644
index 0000000..48d6ee6
--- /dev/null
+++ b/_play/00_README.txt
@@ -0,0 +1,68 @@
+= Welcome to awwan
+
+This is an example of awwan workspace.
+The awwan workspace is indicated by ".ssh" directory, as you can see in the
+list of file in the left.
+
+In awwan, every file is a script, including this file.
+As long as the line is a valid shell command it can execute it.
+
+Lets try.
+
+echo "Hello world" > {{.ScriptDir}}/output
+
+In the input "Execute line" below, set its value to "12" and click on the
+"Local" button.
+You should see output like these,
+
+ 2023/11/29 15:45:08 --> 12: echo "Hello world" > /home/awwan/workspace/output
+
+The same line can be executed in terminal using awwan CLI with following
+command,
+
+awwan local README.txt 12
+
+Click on the directory path "/" on the left top (above ".ssh"), to refresh
+the content of directory.
+You should see a new file "output" (and "README.txt.log") in the list after
+executing above line.
+You ca click on the file "output" to see its content.
+
+That's it!
+
+We provides an example files to follow along, that explain each command and
+feature in the awwan.
+
+01_local.aww - Tutorial on "local" command, to execute command in local
+machine using shell.
+
+02_script_variables.aww - Quick tutorial on global variables that can be
+used in script.
+
+03_env.aww - Tutorial on how to write and use environment file.
+
+04_env-set.aww - Tutorial on "env-set" command, or how to set value into
+environment file.
+
+05_env-get.aww - Tutorial on "env-get" command, or how to get value from
+environment file.
+
+06_magic_put.aww - Tutorial on magic line "#put".
+
+07_magic_get.aww - Tutorial on magic line "#get".
+
+08_encrypt.aww - Tutorial on how to encrypt file and use it to copy file.
+
+09_decrypt.aww - Tutorial on how to decrypt file.
+
+10_encrypted_env.aww - Tutorial on how to use encrypted environment.
+
+11_encrypted_put.aww - Tutorial on how to use magic line "#put" with encrypted
+environment or encrypted file.
+
+12_magic_require.aww - Tutorial on how to use magic line "#require".
+
+remotehost/01_play.aww - Tutorial on how to use "play" command using SSH in
+the server named "remotehost".
+
+remotehost/02_magic_local.aww - Tutorial on magic line "#local".
diff --git a/_play/01_local.aww b/_play/01_local.aww
new file mode 100644
index 0000000..3ec57b1
--- /dev/null
+++ b/_play/01_local.aww
@@ -0,0 +1,50 @@
+The "local" command execute the lines in the host using shell.
+
+In the CLI, the "local" command only have two arguments: the file and comma
+separated line or line range to be executed.
+
+In this web-user interface (WUI) we can run local command by inputting comma
+separated line or line range in the "Execute line" and then click on "Local"
+button.
+
+Let say we have the following lines of commands,
+
+echo "Hello #1"
+
+echo "Hello #2"
+
+echo "Hello #3"
+
+
+To execute line 12 only in the CLI, run
+
+ awwan local 00_local.aww 12
+
+You can try running it in by filling "Execute line" to "12" and clicking
+"Local" button.
+It would print the following output,
+
+ 2023/11/29 17:58:58 --> 12: echo "Hello #1"
+ Hello #1
+
+To execute line 14 until 16,
+
+ awwan local 00_local.aww 14-16
+
+It will print the following output,
+
+ 2023/11/29 18:00:26 --> 14: echo "Hello #2"
+ Hello #2
+ 2023/11/29 18:00:26 --> 16: echo "Hello #3"
+ Hello #3
+
+To execute line 12 and 16 only,
+
+ awwan local 00_local.aww 12,16
+
+It will print the following output,
+
+ 2023/11/29 18:07:10 --> 12: echo "Hello #1"
+ Hello #1
+ 2023/11/29 18:07:10 --> 16: echo "Hello #3"
+ Hello #3
diff --git a/_play/02_script_variables.aww b/_play/02_script_variables.aww
new file mode 100644
index 0000000..44b1192
--- /dev/null
+++ b/_play/02_script_variables.aww
@@ -0,0 +1,28 @@
+There are several global variables that is exported by awwan and accessible
+in the script.
+
+{{.ScriptDir}} - variable that contains the value of script directory.
+
+{{.BaseDir}} - variable that contains the value of base directory, the root
+of awwan workspace.
+
+Both of those variables are accessible in "local" and "play" command.
+
+There are another variables like {{.SSHKey}}, {{.SSHHost}}, {{.SSHPort}},
+and {{.SSHUser}} but only applicable on "play" command so we will discuss it
+later.
+
+Lets try the ScriptDir and BaseDir first.
+
+ echo "Base directory is {{.BaseDir}}"
+ echo "Script directory is {{.ScriptDir}}"
+
+Run both of those lines, you will get the following output,
+
+ 2023/12/02 14:10:09 --> 17: echo "Base directory is /home/awwan/play"
+ 2023/12/02 14:10:09 --> 18: echo "Script directory is /home/awwan/play"
+ Base directory is /home/awwan/play
+ Script directory is /home/awwan/play
+
+Since the script directory is under the workspace, both print the same
+value.
diff --git a/_play/03_env.aww b/_play/03_env.aww
new file mode 100644
index 0000000..56c5574
--- /dev/null
+++ b/_play/03_env.aww
@@ -0,0 +1,57 @@
+Before we play with other other commands, there is one fundamental things
+that we need to understand, the awwan environment.
+
+Awwan environment is stored in file "awwan.env".
+There is another environment file named ".awwan.env.vault" for storing
+encrypted values, but we will discuss it later.
+For now lets just focus on non-encypted environment.
+
+Awwan environment is a key-value storage, formatted using Git INI
+syntax,
+
+ [section "subsection"]
+ key = value
+
+The "subsection" is optional, so one can write
+
+ [section]
+ key = value
+
+The value can span multiple lines by ending it with backslash "\", for
+example
+
+ key_long = multiple \
+ line \
+ value
+
+In any script, we can get the value of key using the following syntax
+
+ {{.Val "section:subsection:key"}}
+
+Lets fill in the "awwan.env" file with the following content,
+
+ [host]
+ name = awwan
+
+ [user "awwan"]
+ name = ms
+
+To get the value of key "name" under section "host",
+
+ echo {{.Val "host::name"}}
+
+Try it, put the line number of above command in "Execute line" and click on
+"Local" button, it should print,
+
+ 2023/12/02 13:54:37 --> 41: echo awwan
+ awwan
+
+To get the value of key "name" in section "user", subsection "awwan",
+
+ echo {{.Val "user:awwan:name"}}
+
+Try it, put the line number of above command in "Execute line" and click on
+"Local" button, it should print,
+
+ 2023/12/02 13:55:42 --> 51: echo ms
+ ms
diff --git a/_play/04_env-set.aww b/_play/04_env-set.aww
new file mode 100644
index 0000000..bc74b20
--- /dev/null
+++ b/_play/04_env-set.aww
@@ -0,0 +1,41 @@
+The "env-set" is the command to set the value in environment file
+"awwan.env".
+
+The syntax is
+
+ <key> <value> <file>
+
+Let say we want to set "host::ip_internal" to "127.0.0.1", run it in the
+terminal as
+
+ awwan env-set host::ip_internal 127.0.0.1 awwan.env
+
+When using this web user interface, we need to prefix the file with variable
+".ScriptDir" or ".BaseDir" depends on where the environment file located.
+Lets try,
+
+ awwan env-set host::ip_internal 127.0.0.1 {{.ScriptDir}}/awwan.env
+
+Run the above line number, you will get
+
+ 2023/12/02 14:21:54 --> 17: awwan env-set host::ip_internal 127.0.0.1 /home/awwan/play/awwan.env
+ 2023/12/02 21:21:54 --- BaseDir: /home/awwan/play
+
+Open the "awwan.env" file, you should see the new key "ip_internal" is
+added under section "host" with value "127.0.0.1",
+
+ cat {{.ScriptDir}}/awwan.env
+
+Output,
+
+ 2023/12/02 14:23:11 --> 27: cat /home/awwan/play/awwan.env
+ ## DO NOT remove this section.
+ [section "subsection"]
+ key = value
+
+ [host]
+ name = awwan
+ ip_internal = 127.0.0.1
+
+ [user "awwan"]
+ name = ms
diff --git a/_play/05_env-get.aww b/_play/05_env-get.aww
new file mode 100644
index 0000000..33352b4
--- /dev/null
+++ b/_play/05_env-get.aww
@@ -0,0 +1,39 @@
+The "env-get" is the command to get the value from environment files,
+"awwan.env" or ".awwan.env.vault" for encrypted file.
+
+The syntax is
+
+ <key> <directory>
+
+Remember, the second parameter is a directory not a file, because the
+environment files are loaded recursively from top to bottom.
+An environment key may not exist in sub directory, but defined in their
+parent directory.
+
+Lets try on the base directory first,
+
+ awwan env-get "host::name" {{.BaseDir}}
+
+It will print,
+
+ 2023/12/04 22:18:06 --- BaseDir: /home/awwan/play
+ 2023/12/04 22:18:06 --- NewSession "."
+ 2023/12/04 22:18:06 --- Loading "awwan.env" ...
+ 2023/12/04 15:18:06 --> 15: awwan env-get "host::name" /home/awwan/play
+ awwan
+
+But if we changes the directory to "remotehost",
+
+ awwan env-get "host::name" {{.BaseDir}}/remotehost
+
+It will print,
+
+ 2023/12/04 15:24:32 --> 29: awwan env-get "host::name" /home/awwan/play/remotehost
+ 2023/12/04 22:24:32 --- BaseDir: /home/awwan/play
+ 2023/12/04 22:24:32 --- NewSession "remotehost"
+ 2023/12/04 22:24:32 --- Loading "awwan.env" ...
+ 2023/12/04 22:24:32 --- Loading "remotehost/awwan.env" ...
+ remotehost
+
+Because the environment variable "host::name" is overridden in "awwan.env"
+file under directory "remotehost".
diff --git a/_play/06_magic_put.aww b/_play/06_magic_put.aww
new file mode 100644
index 0000000..bf6f0e7
--- /dev/null
+++ b/_play/06_magic_put.aww
@@ -0,0 +1,50 @@
+The magic command "#put" is not a CLI command, it is used in the script to
+copy file from source to target.
+
+There are two modes of magic "#put" command, one to copy the file as current user,
+
+ "#put:[+mode] <source> <target>"
+
+and the other one is to copy with sudo,
+
+ "#put![owner][+mode] <source> <target>"
+
+The [owner] option set the target file owner, using "user:group" format.
+The [+mode] option set the target file mode, in octal format, for example +0644.
+
+Lets copy file "put_source.txt" into directory "remotehost",
+
+ #put: {{.BaseDir}}/put_source.txt {{.BaseDir}}/remotehost/put_target.txt
+ cat {{.BaseDir}}/remotehost/put_target.txt
+ ls -l {{.BaseDir}}/remotehost/put_target.txt
+
+It will print the following output,
+
+ 2023/12/04 17:19:07 --> 17: #put: /home/awwan/play/put_source.txt /home/awwan/play/remotehost/put_target.txt
+ 2023/12/04 17:19:07 --> 18: cat /home/awwan/play/remotehost/put_target.txt
+ The host name is awwan.
+ 2023/12/04 17:19:07 --> 19: ls -l /home/awwan/play/remotehost/put_target.txt
+ -rw------- 1 awwan awwan 24 Dec 5 00:19 /home/awwan/play/remotehost/put_target.txt
+
+Take a look at the source "put_source.txt" file and the target
+"remotehost/put_target.txt" file.
+As you can see, the source file can contain variable, which will be replaced
+in the destination file.
+
+Lets copy it using "#put!" and set the owner to user "awwan" and
+group "awwanssh", with permission 0600.
+
+ #put!awwan:awwanssh+0600 \
+ {{.ScriptDir}}/put_source.txt \
+ {{.ScriptDir}}/remotehost/put_target.txt
+ cat {{.ScriptDir}}/remotehost/put_target.txt
+ ls -l {{.ScriptDir}}/remotehost/put_target.txt
+
+The file copied succesfully with user, group, and mode set based on the
+"#put" options,
+
+ 2023/12/04 17:47:40 --> 34: #put!awwan:awwanssh+600 /home/awwan/play/put_source.txt /home/awwan/play/remotehost/put_target.txt
+ 2023/12/04 17:48:55 --> 37: cat /home/awwan/play/remotehost/put_target.txt
+ The host name is awwan.
+ 2023/12/04 17:48:55 --> 38: ls -l /home/awwan/play/remotehost/put_target.txt
+ -rw------- 1 awwan awwanssh 24 Dec 4 17:48 /home/awwan/play/remotehost/put_target.txt
diff --git a/_play/07_magic_get.aww b/_play/07_magic_get.aww
new file mode 100644
index 0000000..6429401
--- /dev/null
+++ b/_play/07_magic_get.aww
@@ -0,0 +1,21 @@
+The magic command "#get" copy file from source to target.
+
+When used with "local" command, it behave like "#put",
+but when used with "play" it copy file from remote server to local host.
+
+Similar to magic command "#put" it have two modes, get file as current
+user "#get:" or get file using sudo "#get!",
+
+ "#get:+mode <target> <source>"
+ "#get!owner+mode <target> <source>"
+
+Lets copy file that can be read by root only into this directory,
+
+ #get!awwan:awwan /etc/shadow {{.ScriptDir}}/get_shadow.txt
+ ls -l {{.ScriptDir}}/get_shadow.txt
+
+We should get the following output,
+
+ 2023/12/04 18:08:26 --> 14: #get!awwan:awwan /etc/shadow /home/awwan/play/get_shadow.txt
+ 2023/12/04 18:09:17 --> 15: ls -l /home/awwan/play/get_shadow.txt
+ -rw------- 1 awwan awwan 586 Dec 4 18:08 /home/awwan/play/get_shadow.txt
diff --git a/_play/08_encrypt.aww b/_play/08_encrypt.aww
new file mode 100644
index 0000000..a32fd0f
--- /dev/null
+++ b/_play/08_encrypt.aww
@@ -0,0 +1,31 @@
+The "encrypt" command encrypt the file using RSA based private key.
+This command require private key file that is stored with name "awwan.key"
+under ".ssh" directory.
+
+The CLI syntax is,
+
+ awwan encrypt <file>
+
+In this workspace we provide the private key with passphrase, see the
+".ssh/awwan.key".
+The passphrase is stored in ".ssh/awwan.pass".
+
+This passphrase file is optional.
+If we remove the passphrase file, awwan will ask passphrase when its running.
+
+In the WUI you can encrypt the file by clicking the "Encrypt" button.
+
+Lets try the CLI command by encrypting the "secret" file in this workspace,
+
+ awwan encrypt secret.txt
+
+Run the above line, it will encrypt the file with name "secret.txt.vault",
+
+ 2023/12/06 14:23:29 --> 20: awwan encrypt secret.txt
+ 2023/12/06 14:23:29 --- BaseDir: /home/awwan/play
+ 2023/12/06 14:23:29 --- Loading passphrase file ".ssh/awwan.pass" ...
+ 2023/12/06 14:23:29 --- Loading private key file ".ssh/awwan.key" (enter to skip passphrase) ...
+ Encrypted file output: secret.txt.vault
+
+Refresh the list of file (or this page) by clicking on the directory "/", you will see
+new file "secret.txt.vault" created.
diff --git a/_play/09_decrypt.aww b/_play/09_decrypt.aww
new file mode 100644
index 0000000..3777e47
--- /dev/null
+++ b/_play/09_decrypt.aww
@@ -0,0 +1,22 @@
+The "decrypt" command decrypt ".vault" file that is encrypted using
+"encrypt" command.
+This command has the following syntax
+
+ awwan decrypt <file.vault>
+
+The file MUST have the ".vault" suffix, otherwise it will be ignored.
+
+Lets try decrypting the previous file that we encrypt,
+
+ awwan decrypt secret.txt.vault
+
+Execute the above line and tt will decrypt the file into "secret.txt",
+
+ 2023/12/06 15:09:55 --> 11: awwan decrypt secret.txt.vault
+ 2023/12/06 15:09:55 --- BaseDir: /home/awwan/play
+ 2023/12/06 15:09:55 --- Loading passphrase file ".ssh/awwan.pass" ...
+ 2023/12/06 15:09:55 --- Loading private key file ".ssh/awwan.key" (enter to skip passphrase) ...
+ Decrypted file output: secret.txt
+
+In the WUI, we can use the "Decrypt" button to decrypt the file directly.
+Select the file to be decrypted and then click "Decrypt" button.
diff --git a/_play/10_encrypted_env.aww b/_play/10_encrypted_env.aww
new file mode 100644
index 0000000..9710812
--- /dev/null
+++ b/_play/10_encrypted_env.aww
@@ -0,0 +1,16 @@
+Now that we know about environment variables and encryption, both of them
+can be combined into storing encrypted environment variables into file
+".awwan.env.vault".
+
+The ".awwan.env.vault" is created from file ".awwan.env" that then
+encrypted.
+
+In this workspace, we provide an example of ".awwan.env.vault".
+Lets get one of the value on it,
+
+ echo {{.Val "user:awwan:pass"}}
+
+If we run it, it will print the value of "user:awwan:pass",
+
+ 2023/12/06 14:45:16 --> 11: echo s3cret
+ s3cret
diff --git a/_play/11_encrypted_put.aww b/_play/11_encrypted_put.aww
new file mode 100644
index 0000000..ceb5372
--- /dev/null
+++ b/_play/11_encrypted_put.aww
@@ -0,0 +1,37 @@
+The magic line "#put" can copy encrypted file or file that contains
+values from encrypted environment variables, ".awwan.env.vault".
+
+In this example we have "secret.txt" that read value
+"user:awwan:pass" which exist only in ".awwan.env.vault".
+
+Lets remove the "secret.txt.vault" first and then copy the file
+to "remotehost",
+
+ rm -f {{.ScriptDir}}/secret.txt.vault
+ #put: {{.ScriptDir}}/secret.txt {{.ScriptDir}}/remotehost/put_secret.txt
+ cat {{.ScriptDir}}/remotehost/put_secret.txt
+
+Run the above three lines, we got
+
+ 2023/12/06 15:32:36 --> 10: rm -f /home/awwan/play/secret.txt.vault
+ 2023/12/06 15:32:36 --> 11: #put: /home/awwan/play/secret.txt /home/awwan/play/remotehost/put_secret.txt
+ 2023/12/06 15:32:36 --> 12: cat /home/awwan/play/remotehost/put_secret.txt
+ My password is s3cret.
+
+The magic line "#put" also can copy whole file that has been encrypted.
+When copying the encrypted file we did not need to add ".vault" suffix,
+awwan will take care of it.
+
+ rm -f {{.ScriptDir}}/app.conf ## Make sure we copy the .vault file.
+ #put: {{.ScriptDir}}/app.conf {{.ScriptDir}}/remotehost/app.conf
+ cat {{.ScriptDir}}/remotehost/app.conf
+
+Run the above two lines and we got,
+
+ 2023/12/06 15:25:29 --> 25: rm -f /home/awwan/play/app.conf ## Make sure we copy the .vault file.
+ 2023/12/06 15:25:29 --> 26: #put: /home/awwan/play/app.conf /home/awwan/play/remotehost/app.conf
+ 2023/12/06 15:25:29 --> 27: cat /home/awwan/play/remotehost/app.conf
+ [database "app"]
+ host = 10.16.1.4
+ user = app
+ pass = pazzw0rd
diff --git a/_play/12_magic_require.aww b/_play/12_magic_require.aww
new file mode 100644
index 0000000..e98361e
--- /dev/null
+++ b/_play/12_magic_require.aww
@@ -0,0 +1,29 @@
+The magic line "#require" is line that will always executed
+when we executed line numbers below it.
+
+For example,
+
+ #require: echo "require #1"
+ echo "Hello after first require"
+ #require: echo "require #2"
+ echo "Hello after second require"
+
+If we execute line 7 only, we got
+
+ 2023/12/06 15:36:10 --- require 6: #require: echo "require #1"
+ require #1
+ 2023/12/06 15:36:10 --> 7: echo "Hello after first require"
+ Hello after first require
+
+The second "#require" require will not get executed.
+
+But if we execute line number 9 only, we got,
+
+ 2023/12/06 15:36:43 --- require 6: #require: echo "require #1"
+ require #1
+ 2023/12/06 15:36:43 --- require 8: #require: echo "require #2"
+ require #2
+ 2023/12/06 15:36:43 --> 9: echo "Hello after second require"
+ Hello after second require
+
+The first and second "#require" will always get executed, in order.
diff --git a/_play/app.conf.vault b/_play/app.conf.vault
new file mode 100644
index 0000000..9032d84
--- /dev/null
+++ b/_play/app.conf.vault
Binary files differ
diff --git a/_play/awwan.env b/_play/awwan.env
index ac4f2d3..97b1383 100644
--- a/_play/awwan.env
+++ b/_play/awwan.env
@@ -1,2 +1,10 @@
+## DO NOT remove this section.
+[section "subsection"]
+key = value
+
+[host]
+name = awwan
+ip_internal = 127.0.0.1
+
[user "awwan"]
name = ms
diff --git a/_play/put_source.txt b/_play/put_source.txt
new file mode 100644
index 0000000..5067c47
--- /dev/null
+++ b/_play/put_source.txt
@@ -0,0 +1 @@
+The host name is {{.Val "host::name"}}.
diff --git a/_play/remotehost/01_play.aww b/_play/remotehost/01_play.aww
new file mode 100644
index 0000000..4384bab
--- /dev/null
+++ b/_play/remotehost/01_play.aww
@@ -0,0 +1,53 @@
+Now we enter the "play" command.
+
+The "play" command execute every line in the remote host
+using SSH.
+The CLI syntax is
+
+ awwan play <path/to/file> <line-range>
+
+Awwan derive the remote host name based on the directory
+names.
+In this script, the directory name is "remotehost".
+We have register the "remotehost" name in ".ssh/config" so
+awwan can know the user and private key file to be used
+to connect to remote host "remotehost",
+
+ Host remotehost
+ Hostname 127.0.0.1
+ Port 20022
+ User awwanssh
+ IdentityFile ~/.ssh/id_ed25519
+
+In this example, the "remotehost" is connected using user "awwanssh"
+with private key in "~/.ssh/id_ed25519".
+
+Lets try execute the following command in remotehost,
+
+ echo "Connect with {{.SSHUser}}@{{.SSHHost}}:{{.SSHPort}} using {{.SSHKey}}"
+
+Using local we got
+
+ 2023/12/06 16:03:32 --> 27: echo "Connect with @: using "
+ Connect with @: using
+
+Because the variable {{.SSHUser}} and others are empty if not running
+under SSH session.
+
+Using CLI,
+
+ awwan play {{.ScriptDir}}/01_play.aww 27
+
+Using WUI, put "27" in "Execute line" and click button "Play".
+We got,
+
+ 2023/12/06 15:45:55 --- SSH identity file: [/home/awwan/.ssh/id_ed25519]
+ 2023/12/06 15:47:10 === BEGIN: remote /remotehost/01_play.aww 27
+ 2023/12/06 15:47:10 --> 27: echo "Connect with awwanssh@127.0.0.1:20022 using /home/awwan/.ssh/id_ed25519"
+ Connect with awwanssh@127.0.0.1:20022 using /home/awwan/.ssh/id_ed25519
+ 2023/12/06 15:47:10 === END: remote /remotehost/01_play.aww 27
+
+The behaviour of other magic lines like "#put" or "#get" are identical
+with local command.
+The magic line "#put" command copy file from local to remote,
+while magic line "#get" copy file from remote to local.
diff --git a/_play/remotehost/02_magic_local.aww b/_play/remotehost/02_magic_local.aww
new file mode 100644
index 0000000..7fe9fbc
--- /dev/null
+++ b/_play/remotehost/02_magic_local.aww
@@ -0,0 +1,25 @@
+The magic line "#local" execute the command in local host
+when script is executed using "play".
+
+ #local: pwd
+ pwd
+
+Running the above two lines will output,
+
+ 2023/12/06 16:30:41 --> 4: #local: pwd
+ /home/awwan/play
+ 2023/12/06 16:30:41 --> 5: pwd
+ /home/awwanssh
+
+The first line print the working directory in local host,
+in this case the current directory.
+While the second line print the working directory of
+remote host, in this case the user home of "awwanssh".
+
+Using "#local" we can combine command that need to be executed
+in local host first and then continue to execute in remote,
+for example by creating directory in local to backup file from
+remote host,
+
+ #local: mkdir -p {{.ScriptDir]}/etc/
+ #get: /etc/hosts {{.ScriptDir}}/etc/hosts
diff --git a/_play/remotehost/awwan.env b/_play/remotehost/awwan.env
new file mode 100644
index 0000000..62d03d0
--- /dev/null
+++ b/_play/remotehost/awwan.env
@@ -0,0 +1,2 @@
+[host]
+name = remotehost
diff --git a/_play/secret.txt b/_play/secret.txt
new file mode 100644
index 0000000..f9e7103
--- /dev/null
+++ b/_play/secret.txt
@@ -0,0 +1 @@
+My password is {{.Val "user:awwan:pass"}}.