---
layout: ../Site.layout.js
---
# Interactive host / visitor kitten live kittendb interaction in embeddable common lisp
A simple case of [kitten](
https://kitten.small-web.org/) server .. kitten visitor interaction is where each kitten pageload increases the number of kittens on the page by one, and the kitten server operator can both read, and arbitrarily change this number live (for the next page load).
<img src="../two-kittens.png">
In practice, different tls authenticated kitten visitors will get different abilities, but to start with, the host and the visitor can interact through kitten's database via kitten's dynamic page generation as you are reading here.
For this example, I am running Kitten's `kitten serve` and `kitten shell` from inside of [embeddable common lisp](
https://ecl.common-lisp.dev/), which can be freely interleaved into C++ programs. They are being started as [external processes](
https://ecl.common-lisp.dev/static/manual/Operating-System-Interface.html#External-processes), and communicated with textually. `kitten shell` uses an (offline javascript) nodejs base. I refered to turning-C++-programs-into-kittens as my [insane kitten fairy godmother theory](/momentary/screwlisps-cl-kitten-future-theory/).
## Lisp `format` and `read-char-no-hang`
I [previously gave an example of reading streams to their end without blocking](/programming/embeddable-common-lisp-external-process-multi-processing-eg/) via [lisp's `read-char-no-hang`](
https://www.lispworks.com/reference/HyperSpec/Body/f_rd_c_1.htm).
Here also, I am sending strings to the `kitten shell` via common lisp's format. If you are not using embeddable common lisp, you can send the kitten/shell commands directly to your shell somehow else.
`(format stream "~a~%" "foo")` sends `foo<newline>` to `stream` stream.
## Kitten
In `#P"~/kittens/dynamicdb/"` I have the following
### `#P"index.page.js"`
```
if (kitten.db.numbers === undefined) kitten.db.numbers = { count: 2 };
export default () => kitten.html`<h1>Kitten count</h1>
<p>${'๐ฑ'.repeat(kitten.db.numbers.count++) }
`
```
being somewhere between the [kitten dynamic page tutorial](
https://kitten.small-web.org/tutorials/dynamic-pages/) and [Kitten persistence tutorial](
https://kitten.small-web.org/tutorials/persistence).
Let us use this from lisp at all.
## Embeddable common lisp `ext:run-program "kitten"`
### Setup
```
#|
(eepitch-shell)
cd ~/kittens/dynamicdb
ecl
|#
```
If you are not using *eepitch* I guess you can figure out what you would do.
## Run Kitten inside of ecl - `kitten serve`
Noting we `cd`(1)'d into our directory already, let's use embeddable common lisp's `ext:run-program` to just start serving the kitten.
```
(locally
(declare (special *kit2way* *kitret* *kitproc*))
(multiple-value-setq
(*kit2way* *kitret* *kitproc*)
(ext:run-program "kitten" '("serve")
:wait nil)))
(loop :for ch := (read-char-no-hang
(two-way-stream-input-stream *kit2way*))
:while ch :do (princ ch))
```
### `kitten serve` output
```
ECL (Embeddable Common-Lisp) 21.2.1 (git:UNKNOWN)
Copyright (C) 1984 Taiichi Yuasa and Masami Hagiya
Copyright (C) 1993 Giuseppe Attardi
Copyright (C) 2013 Juan J. Garcia-Ripoll
Copyright (C) 2018 Daniel Kochmanski
Copyright (C) 2021 Daniel Kochmanski and Marius Gerbershagen
ECL is free software, and you are welcome to redistribute it
under certain conditions; see file 'Copyright' for details.
Type :h for Help.
Top level in: #<process TOP-LEVEL 0x7ff63765df80>.
> (locally
(declare (special *kit2way* *kitret* *kitproc*))
(multiple-value-setq
(*kit2way* *kitret* *kitproc*)
(ext:run-program "kitten" '("serve")
:wait nil)))
#<two-way stream 0x7ff635669500>
>
(loop :for ch := (read-char-no-hang
(two-way-stream-input-stream *kit2way*))
:while ch :do (princ ch))
๐ฑ Kitten
by ]8;;
https://ar.alAral Balkan]8;;, ]8;;
https://small-tech.orgSmall Technology Foundation]8;;
Version 0-046649-22.11.0-20250502014448
Born 2025/05/02 at 01:44:48 UTC (Taurus)
Fav. colour #046649 โโโ
API version 0
Runtime Node.js 22.11.0
โญโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฎ
โ Like this? Fund us! โ
โ โ
โ Weโre a tiny, independent not-for-profit. โ
โ ]8;;
https://small-tech.org/fund-ushttps://small-tech.org/fund-us]8;; โ
โฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฏ
Need help?
]8;;
https://kitten.small-web.org/tutorials/Tutorials]8;; โข ]8;;
https://kitten.small-web.org/reference/Reference]8;; โข ]8;;
https://codeberg.org/kitten/app/issuesIssues]8;;
domainsIncludingRedirectedOnes [ 'localhost' ]
๐ Domain ]8;;
https://localhosthttps://localhost
]8;;๐ Source ]8;;file:///home/me/kittens/dynamicdb๐ /kittens/dynamicdb]8;;
๐พ Data ]8;;/home/me/.local/share/small-tech.org/kitten/data/home.me.kittens.dynamicdb.localhost.443Open app data folder]8;;
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
๐ Donโt forget to create your identity
]8;;
https://localhost/๐/hello/probably-dont-share/One-time use identity creation link]8;;
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Server is running and listening for connectionsโฆ
๐ข Interactive shell is available on 127.0.0.1, port 1337.
๐ข To access it, run kitten shell.
NIL
>
```
Okay! It seems like our kitten is being served. However, the ansi escape codes are just printing, but the kittencode characters are great.
## Start a `kitten shell` as described
```
(locally
(declare (special *2way* *ret* *proc*))
(multiple-value-setq
(*2way* *ret* *proc*)
(ext:run-program "kitten" '("shell")
:wait nil)))
(loop :for ch := (read-char-no-hang
(two-way-stream-input-stream *2way*))
:while ch :do (princ ch))
```
โฌ
```
> (locally
(declare (special *2way* *ret* *proc*))
(multiple-value-setq
(*2way* *ret* *proc*)
(ext:run-program "kitten" '("shell")
:wait nil)))
#<two-way stream 0x7ff634f89b40>
>
(loop :for ch := (read-char-no-hang
(two-way-stream-input-stream *2way*))
:while ch :do (princ ch))
๐ฑ Kitten
by ]8;;
https://ar.alAral Balkan]8;;, ]8;;
https://small-tech.orgSmall Technology Foundation]8;;
Version 0-046649-22.11.0-20250502014448
Born 2025/05/02 at 01:44:48 UTC (Taurus)
Fav. colour #046649 โโโ
API version 0
Runtime Node.js 22.11.0
โญโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฎ
โ Like this? Fund us! โ
โ โ
โ Weโre a tiny, independent not-for-profit. โ
โ ]8;;
https://small-tech.org/fund-ushttps://small-tech.org/fund-us]8;; โ
โฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฏ
Need help?
]8;;
https://kitten.small-web.org/tutorials/Tutorials]8;; โข ]8;;
https://kitten.small-web.org/reference/Reference]8;; โข ]8;;
https://codeberg.org/kitten/app/issuesIssues]8;;
๐ฑ ๐ฌ (127.0.0.1:41028)
NIL
>
```
I guess if we were in a term those [ANSI terminal escape](
https://en.wikipedia.org/wiki/ANSI_escape_code) codes would work.
### Add a `numbers` (javascript) object to the kittendb
If *you have visited* the page, the kitten's page generation will have populated this already. If *not* it will not exist yet. It is not a problem to attempt to run this and fail in the `kitten shell`.
```
(format (two-way-stream-output-stream *2way*)
"~a~%"
"kitten.db.numbers = { count: 3 }")
(loop :for ch := (read-char-no-hang
(two-way-stream-input-stream *2way*))
:while ch :do (princ ch))
```
โฌ
```
> (format (two-way-stream-output-stream *2way*)
"~a~%"
"kitten.db.numbers = { count: 3 }")
NIL
>
(loop :for ch := (read-char-no-hang
(two-way-stream-input-stream *2way*))
:while ch :do (princ ch))
kitten.db.numbers = { count: 3 }
Uncaught:
Error: Table numbers already exists. To replace it, please first call await <database>.numbers.__table__.delete().
at _JSDB.setHandler (file:///home/me/.local/share/small-tech.org/kitten/app/kitten-bundle.js:243715:15)
๐ฑ ๐ฌ (127.0.0.1:41028)
NIL
>
```
### Read the `kittendb` current count
```
(format (two-way-stream-output-stream *2way*)
"~a~%"
"kitten.db.numbers.count")
(loop :for ch := (read-char-no-hang
(two-way-stream-input-stream *2way*))
:while ch :do (princ ch))
```
โฌ <img src="../six-kittens.png">
```
```
### (Interactively) set a new value in the kittendb object
```
(format (two-way-stream-output-stream *2way*)
"~a~%"
"kitten.db.numbers.count = 1")
(loop :for ch := (read-char-no-hang
(two-way-stream-input-stream *2way*))
:while ch :do (princ ch))
```
โฌ
```
> (format (two-way-stream-output-stream *2way*)
"~a~%"
"kitten.db.numbers.count = 1")
NIL
>
(loop :for ch := (read-char-no-hang
(two-way-stream-input-stream *2way*))
:while ch :do (princ ch))
kitten.db.numbers.count = 1
1
๐ฑ ๐ฌ (127.0.0.1:41028)
NIL
```
### Get kittendb value
```
(format (two-way-stream-output-stream *2way*)
"~a~%"
"kitten.db.numbers.count")
(loop :for ch := (read-char-no-hang
(two-way-stream-input-stream *2way*))
:while ch :do (princ ch))
```
โฌ
```
> (format (two-way-stream-output-stream *2way*)
"~a~%"
"kitten.db.numbers.count")
NIL
>
(loop :for ch := (read-char-no-hang
(two-way-stream-input-stream *2way*))
:while ch :do (princ ch))
kitten.db.numbers.count
1
๐ฑ ๐ฌ (127.0.0.1:41028)
NIL
```
### Killing the kitten
Unfortunate heading aside.
```
(format (two-way-stream-output-stream *2way*)
"~a~%"
"process.kill(0)")
```
This seems to end up suddenly killing the whole process for me, after which I might as well
```
#|
(eepitch-kill)
|#
```
for good measure.
# Conclusions
Simple host-visitor communication is seen via the visitor hitting a kitten and the host reading and changing the number of kittens on the kittenloads interactively, live in response.
In practice, the host will be a robot system connecting two visitors' interactions (visitor + host๐ค + visitor), but this article showed one-half of the dynamic in interactive practice (host + visitor)
My model for kittens in lisp is to make an [elephant](
https://common-lisp.net/project/elephant) out of [kittens using the mop](
https://screwlisp.small-web.org/kitten/planning-cl-kitten-mop/).
# Fin.
Kitten steps. What do you think [(on the Mastodon thread please)](
https://gamerplus.org/@screwlisp/114708184726530511) about Kitten, lisp, the example interaction, the direction.