# Why psLens Runs on Go, a Hypermedia UI Stack, and SWS Instead of Living Inside PeopleSoft

> Why psLens uses Go, templ, Datastar, hypermedia, and SWS: safer PeopleSoft access, simpler deployment, and faster iteration than a bolt-on PeopleTools app.

---

LLMS index: [llms.txt](/llms.txt)

---

When I started building psLens, I was not trying to create another PeopleSoft bolt-on. I wanted a tool that could sit beside PeopleSoft, search metadata fast, run audits, monitor Process Scheduler and Integration Broker, and stay easy to deploy and maintain.

That requirement set ruled out a lot of obvious choices.

psLens now runs as a standalone [Go](https://github.com/golang/go) application with server-rendered UI components built in [templ](https://github.com/a-h/templ), live updates handled by [Datastar](https://data-star.dev) ([GitHub](https://github.com/starfederation/datastar)), and a PeopleSoft-side access layer provided by [SWS](https://sws.books.cedarhillsgroup.com/docs/). That combination is not accidental. Each part solves a specific problem that PeopleSoft teams run into when they try to build admin and security tooling the old way.

## Start with the Real Constraint: Safe Access to PeopleSoft

The most important product decision was not Go or Datastar. It was the access boundary.

psLens does not connect directly to the PeopleSoft database. It talks to the SWS framework over HTTPS, and SWS enforces what psLens is allowed to read.

That matters because PeopleSoft environments contain security metadata, operator data, process information, Integration Broker configuration, PeopleCode references, and everything else you would expect in a live enterprise system. If you are going to put a modern web console in front of that, the access path has to be narrow and reviewable.

SWS is what makes that possible.

## SWS Is the Enabler for psLens

SWS is the PeopleSoft-side product that makes psLens practical. It installs as a standard PeopleTools project, exposes a bounded REST API, and restricts queries to a whitelist of PeopleTools metadata tables that the customer reviews and loads.

That gives psLens a few properties that I care about a lot:

- No direct database credentials in the app.
- No write path back into PeopleSoft.
- No ability to query arbitrary tables outside the approved metadata surface.
- A clean HTTPS integration point that works across DEV, TEST, and PROD.

In other words, SWS turns PeopleSoft into something an external tool can query safely. Without it, psLens would either need direct database access, which I did not want, or it would need to live inside PeopleTools, which would drag the whole product back into the world I was trying to get out of.

This is the architecture in one picture:

```mermaid
flowchart LR
    U["Browser"] --> P["psLens\nGo + templ + Datastar"]

    P -->|HTTP SWS requests| PSDEV["PeopleSoft DEV"]
    P -->|HTTP SWS requests| PSTEST["PeopleSoft TEST"]
    P -->|HTTP SWS requests| PSPROD["PeopleSoft PROD"]
```

If you want the short product pitch, this is it: SWS provides the controlled access layer, and psLens turns that access into a usable operations and security console.

## Why Go Fits This Product

Once I knew psLens would be a standalone application, Go became the obvious backend choice.

psLens needs to do a few things at the same time and do them predictably:

- serve normal web requests
- hold open Server-Sent Events connections for live UI updates
- run background alert checks
- generate reports
- stay simple to deploy for managed or self-hosted customers

Go is a good fit for that shape. I get a single compiled binary, straightforward concurrency, fast startup, and a deployment story that stays boring in the best way. That matters when the product may run on [Fly.io](https://fly.io), in a Docker container on a customer VM, or in a tighter self-hosted environment where the operator wants as few moving parts as possible.

I also wanted the codebase to stay readable. A lot of enterprise software ends up with unnecessary framework weight before it even solves the business problem. Go makes it easier to resist that.

## Why I Kept the UI Server-Driven

One thing PeopleSoft got right early was the idea that the server should stay in control of application state. I did not want to throw that away just because I was building outside PeopleTools.

That is why psLens uses `templ` and Datastar instead of a heavy single-page application stack.

More specifically, psLens follows a [hypermedia](https://hypermedia.systems/) approach. The server owns the application state, returns HTML, and drives most interactions directly instead of splitting the product into a separate JavaScript app and JSON API that have to stay in sync.

With `templ`, the UI lives in typed templates that compile into Go. If I reference the wrong field, the build fails. If I rename something in a handler, the compiler catches the breakage.

With Datastar, the browser stays light. The server renders HTML fragments, streams them over Server-Sent Events, and Datastar morphs the DOM in place. I do not need a separate frontend build pipeline, a JSON API for every interaction, or a second copy of application state living in the browser.

That is a good trade for an admin console. The product is about search, inspection, monitoring, and reporting. It is not a drawing canvas or a spreadsheet. I care more about predictable behavior, low client complexity, and keeping sensitive logic on the server than I do about shipping a giant JavaScript bundle.

## Why Not a React Frontend and JSON API?

I could have built psLens as a React app backed by a JSON API. A lot of teams would start there by default because React has become the standard answer for web applications. Personally, I would not have reached for that approach for psLens, but I mention it because it is the comparison most teams expect.

My issue is not React itself so much as the ecosystem gravity around it. Once you go down that path, you usually inherit a larger stack: client-side routing, API clients, state libraries, form libraries, build tooling, hydration concerns, and a second application model in the browser that has to stay aligned with the server. That is a lot of machinery for an admin console.

Hypermedia drastically reduces that complexity by removing whole categories of coordination work. Instead of building a JavaScript application that talks to the server, I can keep the server as the application and let the browser render the result.

In the usual SPA model, every feature expands into multiple layers:

- backend route
- JSON types
- API client code
- client state management
- component rendering logic

For psLens, that split buys me very little. The server already knows the query, the validation rules, the security constraints, and the final HTML I want to send. Datastar lets me keep that hypermedia flow intact.

```mermaid
flowchart LR
    subgraph SPA ["SPA Stack"]
        direction TB
        subgraph SPA_BROWSER ["Browser"]
            s_ui["UI event"]
            s_state["Client state"]
            s_fetch["API request"]
            s_render["Component re-render"]
            s_ui --> s_state --> s_fetch
            s_render --> s_state
        end

        subgraph SPA_SERVER ["Server"]
            s_api["JSON API"]
            s_logic["Validation + business logic"]
            s_data["Server-side state / data"]
            s_api --> s_logic --> s_data
        end

        s_fetch --> s_api
        s_api -->|"JSON response"| s_render
        s_state -. "must stay in sync" .- s_data
    end

    subgraph HM ["psLens Hypermedia Stack"]
        direction TB
        subgraph HM_BROWSER ["Browser"]
            h_ui["UI event"]
            h_req["Datastar request"]
            h_dom["DOM morph"]
            h_ui --> h_req
        end

        subgraph HM_SERVER ["Server"]
            h_handler["Go handler"]
            h_logic["Validation + business logic"]
            h_data["Server-side state / data"]
            h_html["templ HTML fragment"]
            h_handler --> h_logic --> h_data --> h_html
        end

        h_req --> h_handler
        h_html -->|"HTML over SSE"| h_dom
    end
```

The real difference is not just fewer boxes. In the SPA model, both the browser and the server end up owning state that has to stay aligned. In the hypermedia model, the server stays in charge and the browser mostly renders what it is told. That means less code to coordinate, fewer integration seams, and fewer ways for the UI and backend to drift apart.

## Why This Stack Works Well for AI-Assisted Development

There is another advantage here that I did not fully appreciate until I started using AI coding tools heavily: this stack is easier for them to work with.

In psLens, a feature usually lives in one Go handler and one `.templ` file. The query logic, route behavior, and rendered output are close together. The compiler checks the result. That is a much better environment for AI-assisted changes than a stack where one feature is spread across six files in two languages plus an API contract in the middle.

This is not just a developer convenience. It affects product velocity. When the code is local, typed, and testable, I can add object support, reports, and admin features faster without turning the codebase into mush.

There is also a product-side AI angle. psLens exports PeopleSoft objects and reports as structured Markdown, which makes the content usable in tools like ChatGPT, Claude, Claude Code, and Cursor. SWS makes the metadata accessible. The Go and hypermedia stack turns it into a product. The Markdown export makes it portable.

## The Short Answer to “Why Not Build psLens in PeopleTools?”

Because psLens is not trying to be another PeopleSoft application.

It is a separate operations and security console that happens to know a lot about PeopleSoft.

Building it outside PeopleTools gives me normal source control, repeatable deployment, automated tests, easier packaging, faster iteration, and a cleaner path for AI-assisted development. Building it on top of SWS keeps the PeopleSoft access model narrow and reviewable.

I still respect what PeopleTools is good at. If I were building transactional application logic inside PeopleSoft, I would use PeopleTools. But that is not this product. psLens is better as an external web application with a tightly controlled connection back to PeopleSoft.

## Why These Choices Matter Commercially

This stack is not just an engineering preference. It supports the product promises psLens makes to customers.

- Dedicated deployment per customer.
- No direct database access.
- Read-only behavior enforced through SWS.
- Fast iteration without asking customers to rework PeopleSoft customizations.
- Straightforward self-hosting or managed hosting.
- A cleaner bridge from PeopleSoft metadata into modern documentation and AI workflows.

That is the real reason for the architecture. I wanted psLens to feel modern without asking customers to weaken their security posture or turn their PeopleSoft environment into a development host for yet another tool.

## Closing

If you strip away the implementation details, the design comes down to one idea: keep the access path to PeopleSoft tight, and keep the product itself easy to ship.

SWS is what makes the access path credible. Go, `templ`, Datastar, and the hypermedia approach are what make the product practical.

If that combination sounds useful for your PeopleSoft environment, [book a demo](/contact/). I will show you the architecture, the security boundary, and the product running against a live system.

---

### Subscribe for Updates

If you found this article helpful, subscribe to get notified of new PeopleSoft technical notes and psLens updates.











<div class="card bg-light border-0 border-start border-primary border-3 my-4 shadow-sm">
    <div class="card-body p-3">
        <form action="https://subscribe.cedarhillsgroup.com/subscribe" method="POST" class="row align-items-center g-3">
            
            <input type="text" name="website" style="position: absolute; left: -5000px;" tabindex="-1"
                autocomplete="off">
            <input type="hidden" name="redirect_to" value="https://pslens.com/subscribed/">
            <input type="hidden" name="topic_id" value="pslens">

            <div class="col-lg-5">
                <h6 class="mb-1 text-dark fw-bold">Stay Updated</h6>
                <p class="mb-0 text-muted small" style="font-size: 0.85rem;">Receive new articles directly in your inbox.</p>
            </div>

            <div class="col-md-6 col-lg-3">
                <input type="text" name="first_name" placeholder="First Name (optional)"
                    class="form-control form-control-sm">
            </div>

            <div class="col-md-6 col-lg-3">
                <input type="email" name="email" required placeholder="Email Address"
                    class="form-control form-control-sm">
            </div>

            <div class="col-12 col-lg-1">
                <button type="submit" class="btn btn-primary btn-sm w-100">Join</button>
            </div>
        </form>
    </div>
</div>
