Docs
Feed Component
A drop-in Web Component that renders clean, paginated activity feeds anywhere in your product — account settings, team pages, admin views. Built with Angular Elements (zoneless, Shadow DOM), framework-agnostic, ~42 kB gzipped.
Load the script
<script src="https://softechlog.com/feed.js"></script>
Mint a feed token on your server
Public keys are ingest-only, so the browser cannot read activity with one. Instead your backend mints a feed token (stl_ft_…) with the secret key: a short-lived, read-only credential pinned to exactly one actor (and optionally one target). Any page holding it can read only that user's activity — nothing else, and never exports or stats.
// GET /api/activity-token — @softechlog/node
const { token, expires_at } = await log.feedToken({
actorId: req.user.id, // whose activity may be read
ttlSeconds: 3600, // 60 – 86400 (default 3600)
// targetType: "workspace", targetId: workspace.id // optionally pin to one resource
});
res.json({ token, expires_at });# GET /activity-token — softechlog (Python)
@router.get("/activity-token")
async def activity_token(current_user: User = Depends(get_current_user)):
tok = await log.afeed_token(actor_id=str(current_user.id), ttl_seconds=3600)
return {"token": tok.token, "expires_at": tok.expires_at}POST https://api.softechlog.com/v1/feed-tokens
Authorization: Bearer stl_sk_xxxxxxxxxxxx
Content-Type: application/json
{ "actor_id": "user_123", "ttl_seconds": 3600 }
→ 201 { "token": "stl_ft_…", "expires_at": "2026-08-18T15:09:31Z", "actor_id": "user_123" }Tokens live 60 seconds to 24 hours (ttl_seconds, default 3600). When one expires the component shows an "expired" error state — fetch a fresh token from your endpoint and set the attribute again.
Usage
Fetch the token from your backend when the page loads, then set the feed-token attribute. Add target-type / target-id to narrow the feed to one resource:
<!-- Everything the signed-in user did -->
<softechlog-feed id="feed"></softechlog-feed>
<script>
fetch('/api/activity-token')
.then((r) => r.json())
.then(({ token }) => document.getElementById('feed').setAttribute('feed-token', token));
</script>
<!-- Only what they did to one resource -->
<softechlog-feed
feed-token="stl_ft_…"
target-type="workspace"
target-id="ws_abc"
></softechlog-feed>In Angular
<softechlog-feed [attr.feed-token]="feedToken" theme="auto" ></softechlog-feed>
In React
<softechlog-feed feed-token={token} theme="auto" />Attributes
| Attribute | Type | Default | Description |
|---|---|---|---|
feed-token | string | required | Actor-scoped token from POST /v1/feed-tokens |
target-type | string | — | Only events on this resource type |
target-id | string | — | Only events on this resource |
theme | light · dark · auto | auto | auto follows prefers-color-scheme |
limit | number | 25 | Events per page (max 100) |
locale | string | browser | Locale for dates and times |
empty-text | string | No activity yet. | Copy for the empty state |
api-url | string | https://api.softechlog.com | Override the API base URL |
The token already fixes the actor, so there is no actor-id attribute to set — and no attribute can widen the token's scope.
Theming with CSS custom properties
The component renders in Shadow DOM, so your page CSS can't leak in — and its styles can't leak out. Match your design system through custom properties:
softechlog-feed {
--stl-accent: #ee6c2f; /* avatar gradient & interactive colour */
--stl-bg: #ffffff; /* card background */
--stl-text: #0e1a2c; /* primary text */
--stl-muted: #94a3b8; /* secondary / meta text */
--stl-border: #e2e8f0; /* border & divider colour */
--stl-border-radius: 0.75rem; /* outer card corner radius */
--stl-avatar-size: 32px; /* avatar circle diameter */
--stl-font-family: inherit; /* font stack */
}