Received: from mail.netlandish.com (mail.netlandish.com [174.136.98.166])
	by code.netlandish.com (Postfix) with ESMTP id B157F91
	for <~netlandish/links-dev@lists.code.netlandish.com>; Tue, 15 Sep 2026 14:52:35 +0000 (UTC)
Received-SPF: Pass (mailfrom) identity=mailfrom; client-ip=209.85.128.169; helo=mail-yw1-f169.google.com; envelope-from=peter@netlandish.com; receiver=<UNKNOWN> 
Authentication-Results: mail.netlandish.com;
	dkim=pass (1024-bit key; unprotected) header.d=netlandish.com header.i=@netlandish.com header.b=ShIsFVhC
Received: from mail-yw1-f169.google.com (mail-yw1-f169.google.com [209.85.128.169])
	by mail.netlandish.com (Postfix) with ESMTP id 797D91D8185
	for <~netlandish/links-dev@lists.code.netlandish.com>; Tue, 15 Sep 2026 14:53:20 +0000 (UTC)
Received: by mail-yw1-f169.google.com with SMTP id 00721157ae682-8834d10a4e3so5265967b3.1
        for <~netlandish/links-dev@lists.code.netlandish.com>; Tue, 15 Sep 2026 07:53:19 -0700 (PDT)
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
        d=netlandish.com; s=google; t=1789483999; x=1790088799; darn=lists.code.netlandish.com;
        h=content-transfer-encoding:mime-version:message-id:date:subject:cc
         :to:from:from:to:cc:subject:date:message-id:reply-to:content-type;
        bh=7gQsV9L/g6jKfT6zlN8ZphQ+QF9yKAxlbx0+hEq3v0A=;
        b=ShIsFVhCEQP2uezXQs3aGn6kCXPKJzoszdLpzXNGkanbaggSbwG4hW65aQiN2j60DI
         GTwUFBylefpg2njR3HcikU57H/0sRV0gTptXkt24FTFrRAPkh6EfaP2Kb/couB5iK3ma
         iMOyt4QHaJXr/m7GmNaK6gKeYJiCV2+NarUOw=
X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
        d=1e100.net; s=20260707; t=1789483999; x=1790088799;
        h=content-transfer-encoding:mime-version:message-id:date:subject:cc
         :to:from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date
         :message-id:reply-to:content-type;
        bh=7gQsV9L/g6jKfT6zlN8ZphQ+QF9yKAxlbx0+hEq3v0A=;
        b=m0vo3HeI866DJ9Iy1WB33aw7xc5XLX8Wb7ZVtjwa5PmDh+rjHAU3uOTkuSVFVzlaHB
         1xwo24Y6Li+IDz0jmura8a8nTvZxL9jQh9JU59WzX676Bx9Vkrkxzo3UhR45N9Ncd69p
         yHtwhpmcloQeHWosnyMmpQ+GOa4fEjmi9ZAGdgDZp0DGFeobrPPKzQDj4BYSagPDQprO
         fdKfuAf5w7yRW1imZfVHDKhRNqksTLkcGsN6nM0j4HkmqaL9Q+kHOiev0m7I+LtWF7nd
         IxqeLJYqnLu03XwPzA0y7zlarNHOwo0ozrl54l5gkQRzFYBA05/BrFNBVDv6S7089GpA
         2qWA==
X-Gm-Message-State: AFuF++mpfye51bRuPzTl6S2Y8vfv3hr6PrMMat+N/qaywYADmrutg5B1
	/2n9rDfLpHJKDX08wHRrQrMHXOIzVCvFQpGH1vRrxJC6TjkppJ8eUu1Feb6oPMJraLkmhSowlgt
	ANKFZu5U=
X-Gm-Gg: AYBFou2JRONJpgDh+SoB9b2brJLncUNuZkScJkUKZKq9pTyALN2ulSBBf97tCMiD+xl
	Ap9CxuDTiQCRYdE7SxE5v/PfY0+HY+WULRUdVsNl9ObuSgf5q0RjshFu3l2GyM2OKouzL+IsG4D
	dnhSjdAIIqp6cxleDw6iY47O8afAddfOVGGZYGVP5P/TAqH9pSdw5+6e9H8bvtuVfSXKx+Rvjld
	A12aWHRvInP22Kfv0oknB4R0XN+uASCqh5WoxXpde8wMFIHh8BnljYjZkllenwkPGuMMVaMiCvE
	rbbCThtsRxnNA0fkiN5XrzSH3aJlXUM5Aq8YscZaJCHWUQ1GdVD+UHqkgucYPctWbWsVPpJkKkh
	qtOzQBiSYKh7SxNFx2sOU9hWgkQrHRrV6WIhNA2Pjj9hX4aOot6OIzeD1PWUH1isSlg8hzT8sU8
	dFKgwocVU9cUPMANTE1BTrHXGlUM4bn0IlEXI1OsK6ko01Lg/csaloOVwG4QdewTAStbVFIg==
X-Received: by 2002:a05:690c:4d81:b0:873:5c7b:c12c with SMTP id 00721157ae682-88f19993668mr9471527b3.20.1789483999109;
        Tue, 15 Sep 2026 07:53:19 -0700 (PDT)
Received: from localhost ([190.212.216.63])
        by smtp.gmail.com with ESMTPSA id 00721157ae682-88485f91b1csm48013427b3.15.2026.09.15.07.53.18
        (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);
        Tue, 15 Sep 2026 07:53:18 -0700 (PDT)
From: Peter Sanchez <peter@netlandish.com>
To: ~netlandish/links-dev@lists.code.netlandish.com
Cc: Peter Sanchez <peter@netlandish.com>
Subject: [PATCH links] gobwebs bump
Date: Tue, 15 Sep 2026 08:53:12 -0600
Message-ID: <20260915145315.13527-1-peter@netlandish.com>
X-Mailer: git-send-email 2.54.0
MIME-Version: 1.0
Content-Transfer-Encoding: 8bit

Now using RemoteIPMiddleware and supporting functions from gobweb
---
 analytics/helpers.go     |  4 ++--
 analytics/routes_test.go |  3 ++-
 client.go                |  3 ++-
 cmd/api/main.go          |  3 ++-
 cmd/links/main.go        |  2 +-
 cmd/list/main.go         |  3 ++-
 cmd/short/main.go        |  3 ++-
 cmd/test/helpers.go      |  4 ++--
 core/middleware.go       | 11 -----------
 go.mod                   | 12 ++++++------
 go.sum                   | 24 ++++++++++++------------
 helpers.go               | 16 ----------------
 short/routes_test.go     |  3 ++-
 13 files changed, 35 insertions(+), 56 deletions(-)

diff --git a/analytics/helpers.go b/analytics/helpers.go
index dcc346a..4b54281 100644
--- a/analytics/helpers.go
+++ b/analytics/helpers.go
@@ -4,7 +4,6 @@ import (
 	"context"
 	"database/sql"
 	"fmt"
-	"links"
 	"links/models"
 	"net"
 	"net/http"
@@ -12,6 +11,7 @@ import (
 
 	sq "github.com/Masterminds/squirrel"
 	"github.com/oschwald/geoip2-golang"
+	gcore "netlandish.com/x/gobwebs/core"
 	"netlandish.com/x/gobwebs/database"
 )
 
@@ -117,7 +117,7 @@ func AddMetaAnalytics(ctx context.Context, req *http.Request, dailyTotalID int,
 		}
 	}
 
-	ip := links.IPForContext(req.Context())
+	ip := gcore.IPForContext(req.Context())
 	if ip != "" {
 		db, err := geoip2.Open(geoPath)
 		if err != nil {
diff --git a/analytics/routes_test.go b/analytics/routes_test.go
index c6cd46f..eb8b936 100644
--- a/analytics/routes_test.go
+++ b/analytics/routes_test.go
@@ -19,6 +19,7 @@ import (
 	"github.com/labstack/echo/v4"
 	"github.com/stretchr/testify/require"
 	oauth2 "netlandish.com/x/gobwebs-oauth2"
+	gcore "netlandish.com/x/gobwebs/core"
 	"netlandish.com/x/gobwebs/crypto"
 	"netlandish.com/x/gobwebs/server"
 )
@@ -151,7 +152,7 @@ func TestAPI(t *testing.T) {
 		path, ok := srv.Config.File.Get("geo", "path")
 		if ok {
 			// test config has geodb path set. Let's set an IP so we can test the db integration
-			req = req.WithContext(links.IPContext(request.Context(), "142.250.217.196")) // www.google.com
+			req = req.WithContext(gcore.IPContext(request.Context(), "142.250.217.196")) // www.google.com
 		}
 
 		// Add fake analytics entries
diff --git a/client.go b/client.go
index 95f5318..2932a51 100644
--- a/client.go
+++ b/client.go
@@ -10,6 +10,7 @@ import (
 	"git.sr.ht/~emersion/gqlclient"
 	oauth2 "netlandish.com/x/gobwebs-oauth2"
 	"netlandish.com/x/gobwebs/auth"
+	"netlandish.com/x/gobwebs/core"
 	"netlandish.com/x/gobwebs/server"
 )
 
@@ -75,7 +76,7 @@ func Execute(ctx context.Context, op *gqlclient.Operation, result any) error {
 	token := grant.Encode(ctx)
 	trans.AddHeader("Authorization", fmt.Sprintf("Internal %s", token))
 
-	ip := IPForContext(ctx)
+	ip := core.IPForContext(ctx)
 	if ip != "" {
 		trans.AddHeader("X-Real-IP", ip)
 	}
diff --git a/cmd/api/main.go b/cmd/api/main.go
index 0b18c50..f0e8d11 100644
--- a/cmd/api/main.go
+++ b/cmd/api/main.go
@@ -27,6 +27,7 @@ import (
 	oauth2 "netlandish.com/x/gobwebs-oauth2"
 	feedback "netlandish.com/x/gobwebs-ses-feedback"
 	"netlandish.com/x/gobwebs/config"
+	gcore "netlandish.com/x/gobwebs/core"
 	"netlandish.com/x/gobwebs/crypto"
 	"netlandish.com/x/gobwebs/database"
 	"netlandish.com/x/gobwebs/email"
@@ -131,7 +132,7 @@ func run() error {
 		WithQueues(eq, wq).
 		WithMiddleware(
 			database.DBIMiddleware(db),
-			core.RemoteIPMiddleware,
+			gcore.RemoteIPMiddleware,
 			loaders.Middleware(),
 			core.TimezoneContext(),
 			crypto.Middleware(entropy),
diff --git a/cmd/links/main.go b/cmd/links/main.go
index 9e87726..e279193 100644
--- a/cmd/links/main.go
+++ b/cmd/links/main.go
@@ -229,7 +229,7 @@ func run() error {
 		WithQueues(eq, wq, wqi, imq).
 		WithMiddleware(
 			database.DBIMiddleware(db),
-			core.RemoteIPMiddleware,
+			gcore.RemoteIPMiddleware,
 			core.TimezoneContext(),
 			crypto.Middleware(entropy),
 			domain.DomainContext(models.DomainServiceLinks),
diff --git a/cmd/list/main.go b/cmd/list/main.go
index 8ebd05a..58b0245 100644
--- a/cmd/list/main.go
+++ b/cmd/list/main.go
@@ -20,6 +20,7 @@ import (
 	sq "github.com/Masterminds/squirrel"
 	"github.com/labstack/echo/v4"
 	"netlandish.com/x/gobwebs/config"
+	gcore "netlandish.com/x/gobwebs/core"
 	"netlandish.com/x/gobwebs/crypto"
 	"netlandish.com/x/gobwebs/database"
 	"netlandish.com/x/gobwebs/email"
@@ -93,7 +94,7 @@ func run() error {
 		WithQueues(eq, wq).
 		WithMiddleware(
 			database.DBIMiddleware(db),
-			core.RemoteIPMiddleware,
+			gcore.RemoteIPMiddleware,
 			core.TimezoneContext(),
 			crypto.Middleware(entropy),
 			domain.DomainContext(models.DomainServiceList),
diff --git a/cmd/short/main.go b/cmd/short/main.go
index 73e68dc..6b09d08 100644
--- a/cmd/short/main.go
+++ b/cmd/short/main.go
@@ -18,6 +18,7 @@ import (
 	sq "github.com/Masterminds/squirrel"
 	"github.com/labstack/echo/v4"
 	"netlandish.com/x/gobwebs/config"
+	gcore "netlandish.com/x/gobwebs/core"
 	"netlandish.com/x/gobwebs/crypto"
 	"netlandish.com/x/gobwebs/database"
 	"netlandish.com/x/gobwebs/email"
@@ -91,7 +92,7 @@ func run() error {
 		WithQueues(eq, wq).
 		WithMiddleware(
 			database.DBIMiddleware(db),
-			core.RemoteIPMiddleware,
+			gcore.RemoteIPMiddleware,
 			core.TimezoneContext(),
 			crypto.Middleware(entropy),
 			domain.DomainContext(models.DomainServiceShort),
diff --git a/cmd/test/helpers.go b/cmd/test/helpers.go
index f058416..41d4329 100644
--- a/cmd/test/helpers.go
+++ b/cmd/test/helpers.go
@@ -193,7 +193,7 @@ func NewAPITestServer(t *testing.T) (*server.Server, *echo.Echo, string) {
 		DefaultMiddlewareWithConfig(mwConf).
 		WithMiddleware(
 			database.DBIMiddleware(db),
-			core.RemoteIPMiddleware,
+			gcore.RemoteIPMiddleware,
 			core.TimezoneContext(),
 			crypto.Middleware(entropy),
 			core.InternalAuthMiddleware(accounts.NewUserFetch()),
@@ -269,7 +269,7 @@ func getMWChain(s *server.Server, f echo.HandlerFunc, user *models.User) echo.Ha
 	entropy, _ := s.Config.File.Get("access", "entropy")
 	cryptoMiddleware := crypto.Middleware(entropy)
 	handlerFunc := cryptoMiddleware(f)
-	handlerFunc = core.RemoteIPMiddleware(handlerFunc)
+	handlerFunc = gcore.RemoteIPMiddleware(handlerFunc)
 	timezoneMiddleware := core.TimezoneContext()
 	handlerFunc = timezoneMiddleware(handlerFunc)
 	serverMiddleware := server.Middleware(s)
diff --git a/core/middleware.go b/core/middleware.go
index 304bd0c..e8a72f4 100644
--- a/core/middleware.go
+++ b/core/middleware.go
@@ -91,14 +91,3 @@ func CORSReadOnlyMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
 		return next(c)
 	}
 }
-
-func RemoteIPMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
-	return func(c echo.Context) error {
-		c.SetRequest(
-			c.Request().WithContext(
-				links.IPContext(c.Request().Context(), c.RealIP()),
-			),
-		)
-		return next(c)
-	}
-}
diff --git a/go.mod b/go.mod
index be5051f..3b4fd36 100644
--- a/go.mod
+++ b/go.mod
@@ -33,12 +33,12 @@ require (
 	golang.org/x/text v0.27.0
 	golang.org/x/time v0.8.0
 	hg.code.netlandish.com/~netlandish/sendygo v0.0.0-20230124192435-bbf347776232
-	netlandish.com/x/gobwebs v0.1.16
-	netlandish.com/x/gobwebs-auditlog v0.3.3
-	netlandish.com/x/gobwebs-formguard v0.3.1
-	netlandish.com/x/gobwebs-graphql v0.3.1
-	netlandish.com/x/gobwebs-oauth2 v0.2.14
-	netlandish.com/x/gobwebs-ses-feedback v0.2.14
+	netlandish.com/x/gobwebs v0.1.17
+	netlandish.com/x/gobwebs-auditlog v0.3.4
+	netlandish.com/x/gobwebs-formguard v0.3.2
+	netlandish.com/x/gobwebs-graphql v0.3.2
+	netlandish.com/x/gobwebs-oauth2 v0.2.15
+	netlandish.com/x/gobwebs-ses-feedback v0.2.15
 	petersanchez.com/x/carrier v0.2.6
 	petersanchez.com/x/carrier/ses v0.0.0-20260908210515-f3c6132b8e61
 	petersanchez.com/x/carrier/smtp v0.0.0-20260908210515-f3c6132b8e61
diff --git a/go.sum b/go.sum
index f84e607..97447e0 100644
--- a/go.sum
+++ b/go.sum
@@ -2582,18 +2582,18 @@ modernc.org/z v1.0.1-0.20210308123920-1f282aa71362/go.mod h1:8/SRk5C/HgiQWCgXdfp
 modernc.org/z v1.0.1/go.mod h1:8/SRk5C/HgiQWCgXdfpb+1RvhORdkz5sw72d3jjtyqA=
 modernc.org/z v1.2.20/go.mod h1:zU9FiF4PbHdOTUxw+IF8j7ArBMRPsHgq10uVPt6xTzo=
 modernc.org/zappy v1.0.0/go.mod h1:hHe+oGahLVII/aTTyWK/b53VDHMAGCBYYeZ9sn83HC4=
-netlandish.com/x/gobwebs v0.1.16 h1:nUOC73J2PryIm1kesKtcgRTnRmltwUT8/A39gTY/9Yk=
-netlandish.com/x/gobwebs v0.1.16/go.mod h1:2jf9Zn0yiggw+lXtk+aP5PlXa19fExCOvAsxaJPhmv0=
-netlandish.com/x/gobwebs-auditlog v0.3.3 h1:PBwm3hccJ14r6sQxQzl9Vs3GpCjZ6GVEED+8JdqvUkA=
-netlandish.com/x/gobwebs-auditlog v0.3.3/go.mod h1:r1fwrGMp0jOZ/BeAufXRg6pOom1JeaLjTFUU7Mg/WHg=
-netlandish.com/x/gobwebs-formguard v0.3.1 h1:3AdxEcVfErqmFhESVVnyZ4VRcs8xXktBMYOQHLVX/qw=
-netlandish.com/x/gobwebs-formguard v0.3.1/go.mod h1:v/mLy2FMorXagtQk13QUsm/7xQGWTIzjJvSbFRESgYw=
-netlandish.com/x/gobwebs-graphql v0.3.1 h1:cPNITWCD5i9+UBJah50wdniG371y0P/tdCQiRy6StAw=
-netlandish.com/x/gobwebs-graphql v0.3.1/go.mod h1:KMXW4jYeTu6ChyiG7aLHXOtTwMCRgY4ed6NCHS2LtHQ=
-netlandish.com/x/gobwebs-oauth2 v0.2.14 h1:3T3GgO1rO1CeFO2QVK5ndnAehQWHQd54G/cy7BW4PkY=
-netlandish.com/x/gobwebs-oauth2 v0.2.14/go.mod h1:RO8tev6Xy1KBPqGWz6cQBHXUQyE7v2xRMIs4Fd9M2kY=
-netlandish.com/x/gobwebs-ses-feedback v0.2.14 h1:pCH+mLWucC4n6ZYvXBg+FQvfPH3csUEdn6UxPaU/Msg=
-netlandish.com/x/gobwebs-ses-feedback v0.2.14/go.mod h1:8VqQFR4eORxzGcVf4sM0ME4TBWkdqeMw14Itl4j8MGw=
+netlandish.com/x/gobwebs v0.1.17 h1:NHTkicwy3CjlTgSdcgFMNHFpWtpgYwZ08pitL9y71S0=
+netlandish.com/x/gobwebs v0.1.17/go.mod h1:2jf9Zn0yiggw+lXtk+aP5PlXa19fExCOvAsxaJPhmv0=
+netlandish.com/x/gobwebs-auditlog v0.3.4 h1:1KltddWOgy34cHOGpdoCF8LBjsNN447kXs9rkjNtlQs=
+netlandish.com/x/gobwebs-auditlog v0.3.4/go.mod h1:ZHlBBkBGw5OyoHwQp3qiKDbs8EUhRIIdkIc8Nb2do/w=
+netlandish.com/x/gobwebs-formguard v0.3.2 h1:giooK4x7lo+4f8DnlWntS6UmAcvpCzeJO6fkTcVEO7U=
+netlandish.com/x/gobwebs-formguard v0.3.2/go.mod h1:0flzVl44Q6grobYq2eeEfTl7wtrNzoaOT9lkZQcyFGo=
+netlandish.com/x/gobwebs-graphql v0.3.2 h1:/PbHVB8UBSJ7Iv0aoCDatGGBTeflMxW3e5QSptDBchc=
+netlandish.com/x/gobwebs-graphql v0.3.2/go.mod h1:uqmWwIT7MFd9SPkmtNvr8OTSRFH2LLVHRwuq1FL9SA8=
+netlandish.com/x/gobwebs-oauth2 v0.2.15 h1:Ch/5WcEn3iGo2Ml5uLVRMqUYXVL3PkuTTuijJCebWPQ=
+netlandish.com/x/gobwebs-oauth2 v0.2.15/go.mod h1:fJoaihXFmZf8WtMZdTuhoMzBSywrVINnlhjBqWmUY2s=
+netlandish.com/x/gobwebs-ses-feedback v0.2.15 h1:GSdLOd89J1xarfpuadbdAL02z6/BqEiNsvZpy0OmsPI=
+netlandish.com/x/gobwebs-ses-feedback v0.2.15/go.mod h1:f9EvJCa9Zcy0a7o+zuGQ/h8WdLCaURk0RiASLh7Paes=
 petersanchez.com/x/carrier v0.2.6 h1:3siN90JCjMbAdXZ1wh/SakPwwHb4gXj4IghzOSLjJcE=
 petersanchez.com/x/carrier v0.2.6/go.mod h1:GLiDI9OThDmruufk/VHlR6Ihvq/hIJQyA5beU6AFNYk=
 petersanchez.com/x/carrier/ses v0.0.0-20260908210515-f3c6132b8e61 h1:ZX535miQ/79f2IjOqNMhIkJF3nrbvTRaGp7OO7RSuVs=
diff --git a/helpers.go b/helpers.go
index 6617434..690e3b4 100644
--- a/helpers.go
+++ b/helpers.go
@@ -1259,22 +1259,6 @@ func NewRateLimiterConfig(conf *config.Config) (middleware.RateLimiterConfig, er
 	return rlConfig, nil
 }
 
-var IPCtxKey = &contextKey{"remote_ip"}
-
-// IPContext adds a domain model to context for immediate use
-func IPContext(ctx context.Context, ip string) context.Context {
-	return context.WithValue(ctx, IPCtxKey, ip)
-}
-
-// IPForContext fetches current domain from the request context
-func IPForContext(ctx context.Context) string {
-	ip, ok := ctx.Value(IPCtxKey).(string)
-	if !ok {
-		return ""
-	}
-	return ip
-}
-
 // SanitizeUTF8 will strip out invalid utf-8 characters.
 // Invalid UTF-8 bytes are replaced with the Unicode replacement character.
 func SanitizeUTF8(input string) string {
diff --git a/short/routes_test.go b/short/routes_test.go
index ec708fa..ca894be 100644
--- a/short/routes_test.go
+++ b/short/routes_test.go
@@ -19,6 +19,7 @@ import (
 	"github.com/jarcoal/httpmock"
 	"github.com/labstack/echo/v4"
 	"github.com/stretchr/testify/require"
+	gcore "netlandish.com/x/gobwebs/core"
 	"netlandish.com/x/gobwebs/database"
 	"netlandish.com/x/gobwebs/server"
 )
@@ -254,7 +255,7 @@ func TestRedirectHandler(t *testing.T) {
 		_, ok := srv.Config.File.Get("geo", "path")
 		if ok {
 			// test config has geodb path set. Let's set an IP so we can test the db integration
-			request = request.WithContext(links.IPContext(request.Context(), "142.250.217.196")) // www.google.com
+			request = request.WithContext(gcore.IPContext(request.Context(), "142.250.217.196")) // www.google.com
 		}
 
 		recorder := httptest.NewRecorder()
-- 
2.54.0

