LogoPly
Features

Session

Cookie-backed sessions with a pluggable store.

Session

The session package stores per-user values behind a cookie-backed session.

import "github.com/Dziqha/ply/session"

store := session.NewMemoryStore()
manager := session.NewManager(store)

app.Use(manager.Middleware())

NewManager panics if the store is nil.

Read and write

app.Post("/login", func(c *core.Context) {
	manager.Set(c, "user_id", "42")
	c.JSON(200, map[string]string{"status": "ok"})
})

app.Get("/me", func(c *core.Context) {
	userID, _ := manager.Get(c, "user_id").(string)
	if userID == "" {
		c.JSON(401, map[string]string{"error": "not signed in"})
		return
	}
	c.JSON(200, map[string]string{"user_id": userID})
})
MethodEffect
Set(c, key, value)Stores a value in the current session
Get(c, key)Returns a value, or nil
Delete(c, key)Removes one key
Destroy(c)Deletes the session and expires the cookie
Renew(c)Generates a new session ID and keeps the values

Call Renew after a successful login to prevent session fixation. The old ID is deleted when the response is written.

SettingDefault
Nameply_session
Path/
TTL24 hours
Securetrue
HttpOnlytrue
SameSiteLax

For local HTTP development, disable Secure:

manager.Secure = false

Keep HttpOnly enabled. JavaScript does not need access to the session cookie.

Set SameSite explicitly when the application needs cross-site requests:

manager.SameSite = fasthttp.CookieSameSiteNoneMode

SameSite=None requires Secure=true in modern browsers.

Session ID

IDs are 32 random bytes encoded as 64 hexadecimal characters. They are generated with crypto/rand.

The cookie contains only the session ID. Values stay in the store.

Memory store

NewMemoryStore keeps sessions in the current process. It is suitable for one instance, local development, and tests.

Sessions disappear on restart and are not shared between instances. Use a shared store when running more than one process.

MemoryStore copies the top-level value map on save and load. Nested maps and slices are shared, so treat stored values as immutable after Set.

Custom store

Implement three methods to use Redis, a database, or another backend:

type Store interface {
	Load(id string) (map[string]any, error)
	Save(id string, values map[string]any, ttl time.Duration) error
	Delete(id string) error
}

Return session.ErrNotFound when a session does not exist. Any other error from Load produces a 500 response and aborts the request.

Errors from Save and Delete are reported through OnError:

manager.OnError = func(err error) {
	logger.Error("session store", "error", err)
}

When OnError is nil, store errors are ignored. Set it in production.

Expiration

The manager writes the TTL to the store and sets Max-Age on the cookie on every response. The window is sliding: an active session stays alive, and an idle session expires after 24 hours by default. The store must enforce the TTL. MemoryStore checks expiration on load and removes expired entries periodically.

The cookie and the stored session can expire at different times if a custom store does not honor the TTL.

Concurrency

MemoryStore is safe for concurrent use. A custom store must be safe for concurrent requests.

A single session can be loaded by multiple requests at once. The last request to write wins. Avoid storing counters that must be updated atomically in a session; use a database or cache operation for those values.