# Source: https://raw.githubusercontent.com/ServiceStack/docs.servicestack.net/refs/heads/main/MyApp/_pages/lisp-tcp-repl-server.md

---
slug: lisp-tcp-repl-server
title: Lisp TCP REPL Server
---

In addition to launching a [Lisp REPL in the web and app dotnet tools](/dotnet-tool#lisp-repl) you can also open a Lisp REPL into any 
ServiceStack App configured with the `LispReplTcpServer` ServiceStack plugin. This effectively opens a **"programmable gateway"** into any 
ServiceStack App where it's able to perform live queries, access IOC dependencies, invoke internal Server functions and query
the state of a running Server which like the [Debug Inspector](/debugging#debug-inspector) 
can provide invaluable insight when diagnosing issues on a remote server.

To see it in action we'll enable it one of our production Apps [techstacks.io](https://techstacks.io) which as it's a 
Vuetify SPA App is only configured with an empty `SharpPagesFeature` as it doesn't use any server-side scripting features.

We'll enable it in `DebugMode` where we can enable by setting `DebugMode` in our App's `appsettings.Production.json` 
which will launch a TCP Socket Server which by default is configured to listen to the **loopback** IP on port `5005`.

```csharp
if (Config.DebugMode)
{
    Plugins.Add(new LispReplTcpServer {
        ScriptMethods = {
            new DbScripts()
        },
        ScriptNamespaces = {
            nameof(TechStacks),
            $"{nameof(TechStacks)}.{nameof(ServiceInterface)}",
            $"{nameof(TechStacks)}.{nameof(ServiceModel)}",
        },
    });
}
```

::: info
[ScriptNamespaces](https://sharpscript.net/docs/script-net#type-resolution) behaves like C#'s `using Namespace;` statement letting you reference Types by `Name` instead of its fully-qualified Namespace
:::

Whilst you can now connect to it with basic `telnet`, it's a much nicer experience to use it with the [rlwrap](https://linux.die.net/man/1/rlwrap)
readline wrap utility which provides an enhanced experience with line editing, persistent history and completion.

:::sh
sudo apt-get install rlwrap
:::

Then you can open a TCP Connection to connect to a new Lisp REPL with:

:::sh
rlwrap telnet localhost 5005
:::

Where you now have full scriptability of the running server as allowed by [#Script Pages](https://sharpscript.net/docs/script-pages) `SharpPagesFeature` which
allows [scripting of all .NET Types](https://sharpscript.net/docs/script-net#allowscriptingofalltypes) by default. 

## TechStacks TCP Lisp REPL Demo

In this demo we'll explore some of the possibilities of scripting the live [techstacks.io](https://techstacks.io) Server where we can 
`resolve` IOC dependencies to send out tweets using its registered `ITwitterUpdates` dependency, view the source and load a remote 
[parse-rss](https://gist.github.com/gistlyn/3624b0373904cfb2fc7bb3c2cb9dc1a3) lisp function into the new Lisp interpreter attached to the TCP connection, 
use it to parse [Hacker News RSS Feed](https://news.ycombinator.com/rss) into a .NET Collection where it can be more easily queried using its built-in functions
which is used to construct an email body with **HN's current Top 5 links**. 

It then uses [DB Scripts](https://sharpscript.net/docs/db-scripts) to explore its configured AWS RDS PostgreSQL RDBMS, listing its DB tables and viewing its 
column names and definitions before retrieving the Email addresses of all **Admin** users, sending them each an email with HN's Top 5 Links by 
publishing **5x** `SendEmail` Request DTOs using the [publishMessage](https://sharpscript.net/docs/servicestack-scripts#publishmessage) ServiceStack Script to where 
they're processed in the background by its configured [MQ Server](/messaging) that uses it to execute the 
`SendEmail` ServiceStack Service where it uses its configured AWS SES SMTP Server to finally send out the Emails:

[![](/img/pages/sharpscript/lisp-tcp-repl.gif)](https://youtu.be/HO523cFkDfk)

::: info YouTube
[youtu.be/HO523cFkDfk](https://youtu.be/HO523cFkDfk)
:::

## Password Protection 

Since TCP Server effectively opens your remote Server up to being scripted you'll want to ensure the TCP Server is only accessible
within a trusted network, effectively treating it the same as [Redis Security Model](https://redis.io/topics/security).

A secure approach would be to leave the default of only binding to `IPAddress.Loopback` so only trusted users with SSH access will 
be able to access it, which they'll still be able to access remotely via `Local PC > ssh > telnet 127.0.0.1 5005`.

Just like [Redis AUTH](https://redis.io/commands/auth) you can also add password protection for an additional layer of Security:

```csharp
Plugins.Add(new LispReplTcpServer {
    RequireAuthSecret = true,
    ...
});
```

Which will only allow access to users with the [configured AuthSecret](/debugging#authsecret):

```csharp
SetConfig(new HostConfig { 
    AdminAuthSecret = "secretz" 
});
```

## Annotated Lisp TCP REPL Transcript

```lisp
; resolve `ITwitterUpdates` IOC dependency and assign it to `twitter`
(def twitter (resolve "ITwitterUpdates"))

; view its concrete Type Name
(typeName twitter)

; view its method names 
(joinln (methods twitter))

; view its method signatures 
(joinln (methodTypes twitter))

; use it to send tweet from its @webstacks account
(.Tweet twitter "Who's using #Script Lisp? https://sharpscript.net/lisp")

; view all available scripts in #Script Lisp Library Index gist.github.com/3624b0373904cfb2fc7bb3c2cb9dc1a3
(gistindex)

; view the source code of the `parse-rss` library
(load-src "index:parse-rss")

; assign the XML contents of HN's RSS feed to `xml`
(def xml (urlContents "https://news.ycombinator.com/rss"))

; preview its first 1000 chars
(subString xml 0 1000)

; use `parse-rss` to parse the RSS feed into a .NET Collection and assign it to `rss`
(def rss (parse-rss xml))

; view the `title`, `description` and the first `item` in the RSS feed:
(:title rss)
(:description rss)
(:0 (:items rss))

; view the links of all RSS feed items
(joinln (map :link (:items rss)))

; view the links and titles of the top 5 news items
(joinln (map :link (take 5 (:items rss))))
(joinln (map :title (take 5 (:items rss))))

; construct a plain-text numbered list of the top 5 HN Links and assign it to `body`
(joinln (map-index #(str %2 (:title %1)) (take 5 (:items rss))))
(joinln (map-index #(str (padLeft (1+ %2) 2) ". " (:title %1)) (take 5 (:items rss))))
(def body (joinln 
    (map-index #(str (padLeft (1+ %2) 2) ". " (:title %1) "\n" (:link %1) "\n") (take 5 (:items rss)))))

; view all TechStacks PostgreSQL AWS RDS tables
(dbTableNames)
(joinln dbTableNames)

; view the column names and definitions of the `technology` table
(joinln (dbColumnNames "technology"))
(joinln (dbColumns "technology"))

; search for all `user` tables
(globln "*user*" (dbTableNames))

; view how many Admin Users with Emails there are
(dbScalar "select count(email) from custom_user_auth where roles like '%Admin%'")

; assign the Admin Users email to the `emails` list
(def emails (map :email (dbSelect "select email from custom_user_auth where roles like '%Admin%'")))

; search for all `operation` script methods
(globln "*operation*" scriptMethods)

; search for all `email` Request DTOs
(globln "*email*" metaAllOperationNames)

; view the properties available on the `SendEmail` Request DTO
(props (SendEmail.))

; search for all `publish` script methods that can publish messages
(globln "publish*" scriptMethods)

; create and publish 5x `SendEmail` Request DTOs for processing by TechStacks configured MQ Server
(doseq (to emails) (publishMessage "SendEmail" { :To to :Subject "Top 5 HN Links" :Body body }))
```