56 results available
Sign in
Skills directory

go-cache

encoredev

·

Last changed
Hardened

This skill has been hardened by Chainguard

Last changed

This version passed the hardening gate. Hardening updated the skill content and independently checked the resulting changes. Classified findings before hardening: 0. Classified findings closed: 0. Classified findings remaining: 0. Unclassified scanner findings remaining: 0. Review findings closed: 9. Review findings remaining: 1.


Open before hardening

None

Open after hardening

None


Expand
File
Group
Subtype
Severity
Status

No findings available

0 findings


SKILL.md

Encore Go Caching (Redis)

Instructions

Encore Go's cache is a typed wrapper around Redis. Declare a cache.Cluster once, then create Keyspace objects for each shape of data you need.

Cluster

package mycache

import "encore.dev/storage/cache"

var Cluster = cache.NewCluster("my-cache", cache.ClusterConfig{
    EvictionPolicy: cache.AllKeysLRU,
})

Eviction policies: cache.AllKeysLRU (default), cache.NoEviction, cache.AllKeysLFU, cache.AllKeysRandom, cache.VolatileLRU, cache.VolatileLFU, cache.VolatileTTL, cache.VolatileRandom.

Clusters holding rate limit counters or session state should use cache.NoEviction, a volatile policy with an expiry on every key, or a dedicated cluster, so that eviction under memory pressure keeps that state intact.

Keyspace types

Each keyspace has a key shape (used to build the Redis key from KeyPattern) and a value type.

package mycache

import (
    "context"
    "time"
    "encore.dev/storage/cache"
)

type TokenKey struct {
    TokenID string
}

// Strings
var Tokens = cache.NewStringKeyspace[TokenKey](Cluster, cache.KeyspaceConfig{
    KeyPattern:    "token/:TokenID",
    DefaultExpiry: cache.ExpireIn(time.Hour),
})

func example(ctx context.Context) {
    _ = Tokens.Set(ctx, TokenKey{TokenID: "abc"}, "value")
    val, err := Tokens.Get(ctx, TokenKey{TokenID: "abc"}) // cache.Miss on miss
    _ = Tokens.Delete(ctx, TokenKey{TokenID: "abc"})
    _ = val
    _ = err
}
// Integers (atomic counters)
type CounterKey struct {
    UserID string
}

var Counters = cache.NewIntKeyspace[CounterKey](Cluster, cache.KeyspaceConfig{
    KeyPattern:    "requests/:UserID",
    DefaultExpiry: cache.ExpireIn(10 * time.Second),
})

func incr(ctx context.Context) {
    count, _ := Counters.Increment(ctx, CounterKey{UserID: "user123"}, 1)
    _, _ = Counters.Decrement(ctx, CounterKey{UserID: "user123"}, 1)
    _ = count
}
// Structs (JSON-encoded)
type ProfileKey struct {
    UserID string
}

type UserProfile struct {
    Name  string
    Email string
}

var Profiles = cache.NewStructKeyspace[ProfileKey, UserProfile](Cluster, cache.KeyspaceConfig{
    KeyPattern:    "profile/:UserID",
    DefaultExpiry: cache.ExpireIn(time.Hour),
})

func setProfile(ctx context.Context) {
    _ = Profiles.Set(ctx, ProfileKey{UserID: "123"}, UserProfile{
        Name: "Alice", Email: "alice@example.com",
    })
}

The examples above discard errors only for brevity. In real code, check every returned error from Set, Get, Delete, Increment and Decrement, and handle cache.Miss separately from other Get errors. Treat a failed Delete used to revoke a token as an error that must be handled.

Other keyspace types

All from encore.dev/storage/cache:

  • NewFloatKeyspace — float64 values, has Increment.
  • NewListKeyspace — list values, with PushLeft/PushRight/PopLeft/PopRight/GetRange.
  • NewSetKeyspace — set values, with Add/Remove/Contains/Items.

Multi-field key patterns

type ResourceKey struct {
    UserID       string
    ResourcePath string
}

var ResourceRequests = cache.NewIntKeyspace[ResourceKey](Cluster, cache.KeyspaceConfig{
    KeyPattern:    "requests/:UserID/:ResourcePath",
    DefaultExpiry: cache.ExpireIn(10 * time.Second),
})

Validate key fields taken from user input (reject the slash character) or encode or hash them before use, and take UserID from the authenticated identity rather than from request input, so that keys stay distinct and limits apply as intended.

Expiry helpers

import (
    "encore.dev/storage/cache"
    "time"
)

cache.ExpireIn(time.Hour)              // relative
cache.ExpireDailyAt(2, 0, 0, time.UTC) // specific UTC time each day
cache.NeverExpire                      // no expiry
cache.KeepTTL                          // keep existing TTL when updating

For tokens or personal data, use a bounded expiry such as cache.ExpireIn instead of cache.NeverExpire or cache.KeepTTL.

Errors

import "encore.dev/storage/cache"

val, err := keyspace.Get(ctx, key)
if errors.Is(err, cache.Miss) {
    // not in cache
}

err = keyspace.SetIfNotExists(ctx, key, value)
if errors.Is(err, cache.KeyExists) {
    // already there
}

This snippet also needs the standard errors package imported; ctx, key, value and keyspace are placeholders for your own values.

Guidelines

  • Declare cache.Cluster and keyspaces as package-level variables.
  • Pick the most specific keyspace type — IntKeyspace for counters gives you atomic Increment/Decrement for free.
  • Get() returns cache.Miss on miss; Replace() returns cache.Miss when the key is absent, and SetIfNotExists() returns cache.KeyExists when the key already exists.
  • When a counter enforces a rate limit, always check the error from Increment and Decrement, and choose deliberately whether to reject the request or allow it when the cache is unavailable. For security sensitive limits, reject the request.
  • For tokens, key entries by a hash of the token rather than the raw secret, store only minimal non secret data, cache personal data such as email only when required, and always set a bounded expiry for sensitive entries.
  • Treat the cache as a hint for token validity or revocation: when an entry is missing, evicted, or the cache returns an error, confirm against durable storage or deny the request.
  • Local development uses an in-memory Redis with a ~100-key cap — don't load-test it.
  • For durable storage, use encore-go-database (Postgres) or encore-go-bucket (object storage) instead.

Install

Install with chainctl
chainctl skill install public/encoredev/go-cache:latest
Get started with chainctl
View source

Harden your own skills

Bring Chainguard's hardening pipeline to your first-party skills — custom rulesets, automated remediation, full audit trail.

Provenance

Upstream sourcehttps://github.com/encoredev/skills
Upstream commitcb69bb1f246274419aed69c84ee5815955c4c499
OCI reference

skills.cgr.dev/public/github.com/encoredev/encore/go-cache:latest

License

Apache-2.0

Files

HARDENING.md

LICENSE

SKILL.md


The trusted source for open source

Talk to an expert
PrivacyTerms

Product

Chainguard ContainersChainguard LibrariesChainguard VMsChainguard OS PackagesChainguard ActionsChainguard Agent SkillsIntegrationsPricing
© 2026 Chainguard, Inc. All Rights Reserved.
Chainguard® and the Chainguard logo are registered trademarks of Chainguard, Inc. in the United States and/or other countries.
The other respective trademarks mentioned on this page are owned by the respective companies and use of them does not imply any affiliation or endorsement.