# What Is the Role of the Server Layer in Hister? A Deep Dive into the HTTP Architecture

> Discover the server layer's role in Hister. It transforms the indexing library into a production web service, managing HTTP, routing, security, sessions, and static assets.

- Repository: [Adam Tauber/hister](https://github.com/asciimoo/hister)
- Tags: deep-dive
- Published: 2026-09-01

---

**The server layer in Hister acts as the orchestration backbone that transforms the core indexing library into a production-ready web service, handling HTTP bootstrapping, request routing, security middleware, session management, and static asset delivery.**

The server layer sits at the heart of the `asciimoo/hister` architecture, bridging the gap between low-level indexing operations and user-facing HTTP interactions. Understanding the role of the server layer in Hister is essential for developers who want to extend the REST API, customize authentication flows, or deploy the application behind reverse proxies. Unlike the indexer or extractor packages that handle data processing, the server package focuses exclusively on request lifecycle management and protocol handling.

## Bootstrapping the HTTP Server

The entry point for all network traffic begins in [`server/server.go`](https://github.com/asciimoo/hister/blob/main/server/server.go) with the `Listen` function (lines 44-66). This function initializes the complete HTTP stack before calling `http.ListenAndServe`.

During startup, `Listen` performs three critical initialization steps:

1. **Secret key loading** – Reads the application secret for cryptographic operations
2. **Session store preparation** – Creates a cookie-based session store (lines 31-38) that persists authentication state across requests
3. **Indexer injection** – Accepts an `indexer.Indexer` instance to delegate search and document operations

The server remains the exclusive owner of the HTTP socket, ensuring that all business logic remains decoupled from transport concerns.

## Routing and Endpoint Registration

Once initialized, the server constructs the request router through `registerEndpoints` in [`server/endpoints.go`](https://github.com/asciimoo/hister/blob/main/server/endpoints.go) (lines 61-84). This function builds an `http.ServeMux` and binds every REST API endpoint to its URL pattern.

Each endpoint is defined by the `Endpoint` struct, which specifies:

- **Path and Method** – The URL route and HTTP verb
- **CSRF requirements** – Whether the endpoint requires token validation
- **Access control** – Public, user, or admin authorization levels
- **Handler function** – The business logic implementation

The registration process wraps each handler with middleware layers before adding it to the mux, creating a composable security pipeline that executes before request processing begins.

## Security Middleware and Session Management

The server layer enforces security policies through a chain of middleware functions that intercept every request.

### CSRF Protection

The `withCSRF` middleware (lines 40-55 in [`server/endpoints.go`](https://github.com/asciimoo/hister/blob/main/server/endpoints.go)) validates CSRF tokens for unsafe HTTP methods (POST, PUT, DELETE). It injects a fresh token into each response, preventing cross-site request forgery attacks against authenticated users.

### Authentication Layers

Depending on the configuration, handlers are decorated with one of three auth wrappers (lines 66-80):

- **`withUserAuth`** – Requires an active user session
- **`withAdminAuth`** – Requires administrative privileges
- **`withTokenAuth`** – Validates a static access token for API clients

### Session State

The server maintains session continuity through a cookie-based store created during `Listen`. Each request refreshes the session cookie, allowing the server to track authenticated users across stateless HTTP calls without requiring re-authentication.

## Static Asset Delivery and SPA Handling

Beyond API endpoints, the server serves the embedded Svelte frontend application. It parses the embedded filesystem (`static.FS`), injects runtime configuration including the base path, and delivers JavaScript, CSS, and HTML assets with appropriate cache-control headers.

The `serveSPA` function handles Single Page Application navigation by falling back to [`index.html`](https://github.com/asciimoo/hister/blob/main/index.html) for unknown routes, enabling client-side routing to function correctly regardless of the specific URL accessed.

### Base-Path Prefix Support

When deployed under a sub-directory (e.g., `/hister/` behind a reverse proxy), the `withOptionalBasePathPrefix` middleware (lines 42-50) automatically strips the configured prefix from incoming URLs before routing. This allows internal handlers to remain agnostic of deployment topology while external clients access the service at prefixed paths.

## Business Logic Orchestration

While the server layer does not implement core indexing algorithms, it orchestrates the flow between HTTP requests and the underlying `indexer.Indexer` methods. Each high-level operation has a dedicated handler:

- **`serveSearch`** – Extracts query parameters, calls `indexer.Search`, and formats results as JSON or RSS
- **`serveAdd`** – Validates document payloads, applies extraction rules, and invokes `indexer.AddContext`
- **`serveHistory`** – Retrieves document versioning information from the model layer

These handlers enforce policy (rate limiting, size constraints) and handle serialization, ensuring that the indexer package remains pure business logic without HTTP dependencies.

## Extending the Server Layer

You can extend the HTTP interface by adding custom endpoints to the global `Endpoints` slice before the server starts.

### Starting the Server

From the main package, bootstrap the server with a configuration and indexer instance:

```go
// main.go (simplified)
cfg, _ := config.Load()
idx, _ := indexer.New(cfg.Indexer)
server.Listen(cfg, idx)   // ← boots the HTTP server

```

### Adding a Custom Endpoint

Define a new endpoint by implementing the `Endpoint` struct and registering it:

```go
// custom.go
var CustomEndpoint = server.Endpoint{
    Name:        "myinfo",
    Path:        "/api/myinfo",
    Method:      http.MethodGet,
    CSRFRequired: false,
    Public:      true,
    Description: "Returns custom status information",
    Handler: func(c *server.WebContext) {
        c.JSON(map[string]string{"status": "OK"})
    },
}

func init() {
    server.Endpoints = append(server.Endpoints, CustomEndpoint)
}

```

### Configuring Sub-Path Deployment

To serve Hister under a sub-directory, set the base path in your configuration:

```yaml

# config.yaml

app:
  base_path: "/hister"

```

Requests to `https://example.com/hister/search?q=test` are automatically handled as `/search?q=test` internally.

## Summary

- The **server layer** converts Hister from a library into a web service by managing the HTTP lifecycle in [`server/server.go`](https://github.com/asciimoo/hister/blob/main/server/server.go) and [`server/endpoints.go`](https://github.com/asciimoo/hister/blob/main/server/endpoints.go).
- **Bootstrapping** occurs through the `Listen` function, which initializes session stores and starts the HTTP listener.
- **Routing** is handled by `registerEndpoints`, which binds handlers to URL patterns and composes middleware chains.
- **Security** is enforced through `withCSRF`, `withUserAuth`, `withAdminAuth`, and `withTokenAuth` middleware that validates tokens and sessions.
- **Static assets** are served from an embedded filesystem with SPA fallback support and optional base-path prefix stripping.
- **Business logic** handlers delegate to the `indexer.Indexer` and model layers while handling HTTP-specific concerns like parameter extraction and response formatting.

## Frequently Asked Questions

### How does the server layer protect against CSRF attacks?

The server uses the `withCSRF` middleware in [`server/endpoints.go`](https://github.com/asciimoo/hister/blob/main/server/endpoints.go) (lines 40-55) to validate CSRF tokens on all unsafe HTTP methods. It compares the token from the request header against the session-stored value and injects a fresh token into every response, ensuring that state-changing requests originate from legitimate sessions.

### Can I run Hister without the server layer as a standalone library?

Yes. The server layer is optional; the core functionality lives in the `indexer` and `extractor` packages. You can import `github.com/asciimoo/hister/indexer` directly into your Go application and call methods like `AddContext` or `Search` programmatically without starting the HTTP server.

### How do I add custom authentication to the Hister server?

You can implement custom authentication by creating a new middleware wrapper similar to `withUserAuth` in [`server/endpoints.go`](https://github.com/asciimoo/hister/blob/main/server/endpoints.go) (lines 66-80). Your middleware should validate your custom token or session mechanism, set the user context on the request, and then call the next handler. Register your middleware in the `registerEndpoints` function to protect specific routes.

### What is the purpose of the base-path prefix configuration?

The base-path prefix allows Hister to operate behind reverse proxies that route requests under sub-directories (e.g., `/hister/`). The `withOptionalBasePathPrefix` middleware (lines 42-50) strips this prefix from incoming URLs before routing, ensuring that internal handlers receive clean paths while external URLs remain correctly prefixed.