clickwrap 0.0.0 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rubocop.yml +90 -0
- data/CHANGELOG.md +612 -0
- data/README.md +830 -1204
- data/SECURITY.md +33 -0
- data/app/assets/stylesheets/clickwrap.css +241 -0
- data/app/controllers/clickwrap/application_controller.rb +79 -0
- data/app/controllers/clickwrap/captures_controller.rb +145 -0
- data/app/controllers/clickwrap/document_versions_controller.rb +71 -0
- data/app/controllers/clickwrap/receipts_controller.rb +115 -0
- data/app/controllers/clickwrap/withdrawals_controller.rb +60 -0
- data/app/helpers/clickwrap/engine_helper.rb +97 -0
- data/app/views/clickwrap/captures/show.html.erb +34 -0
- data/app/views/clickwrap/receipts/index.html.erb +38 -0
- data/app/views/clickwrap/receipts/show.html.erb +91 -0
- data/app/views/clickwrap/shared/_error_summary.html.erb +39 -0
- data/app/views/clickwrap/shared/_fields.html.erb +100 -0
- data/app/views/clickwrap/shared/_statement.html.erb +105 -0
- data/app/views/clickwrap/withdrawals/new.html.erb +30 -0
- data/config/locales/en.yml +160 -0
- data/config/locales/es.yml +138 -0
- data/config/routes.rb +41 -0
- data/exe/clickwrap +374 -0
- data/guides/README.md +30 -0
- data/guides/accessibility.md +249 -0
- data/guides/consent-and-lifecycle.md +299 -0
- data/guides/integrating.md +610 -0
- data/guides/integrity.md +212 -0
- data/guides/migrating.md +335 -0
- data/guides/naming.md +320 -0
- data/guides/organizations.md +320 -0
- data/guides/receipts-and-verification.md +415 -0
- data/guides/request-evidence.md +512 -0
- data/guides/retention-and-legal-holds.md +438 -0
- data/lib/clickwrap/actor_proxy.rb +147 -0
- data/lib/clickwrap/anonymous_actor.rb +47 -0
- data/lib/clickwrap/authority.rb +174 -0
- data/lib/clickwrap/canonical_json.rb +216 -0
- data/lib/clickwrap/capture/event_builder.rb +220 -0
- data/lib/clickwrap/capture/presentation_verifier.rb +521 -0
- data/lib/clickwrap/capture.rb +650 -0
- data/lib/clickwrap/configuration.rb +1129 -0
- data/lib/clickwrap/controller_helpers.rb +758 -0
- data/lib/clickwrap/current_state.rb +282 -0
- data/lib/clickwrap/digest.rb +125 -0
- data/lib/clickwrap/doctor.rb +418 -0
- data/lib/clickwrap/document_definition.rb +255 -0
- data/lib/clickwrap/document_renderer.rb +83 -0
- data/lib/clickwrap/document_renderers/markdown.rb +175 -0
- data/lib/clickwrap/document_renderers/markdown_rails.rb +126 -0
- data/lib/clickwrap/dsl/policy_builder.rb +462 -0
- data/lib/clickwrap/dsl/retention_builder.rb +89 -0
- data/lib/clickwrap/durable_commit_callback.rb +37 -0
- data/lib/clickwrap/engine.rb +184 -0
- data/lib/clickwrap/errors.rb +181 -0
- data/lib/clickwrap/form_builder_extensions.rb +341 -0
- data/lib/clickwrap/front_matter.rb +67 -0
- data/lib/clickwrap/identifier.rb +112 -0
- data/lib/clickwrap/import/external_receipt.rb +241 -0
- data/lib/clickwrap/import/fine_print.rb +290 -0
- data/lib/clickwrap/import/legacy.rb +450 -0
- data/lib/clickwrap/integrations/organizations_authority.rb +81 -0
- data/lib/clickwrap/integrity/anchor.rb +130 -0
- data/lib/clickwrap/integrity/attestation_reconciler.rb +114 -0
- data/lib/clickwrap/integrity/attestor.rb +221 -0
- data/lib/clickwrap/integrity/chain.rb +313 -0
- data/lib/clickwrap/integrity/timestamp.rb +143 -0
- data/lib/clickwrap/ip_geolocation/location.rb +112 -0
- data/lib/clickwrap/ip_geolocation/null_resolver.rb +35 -0
- data/lib/clickwrap/ip_geolocation/resolver.rb +97 -0
- data/lib/clickwrap/ip_geolocation/static_resolver.rb +107 -0
- data/lib/clickwrap/ip_geolocation/trackdown_resolver.rb +330 -0
- data/lib/clickwrap/ip_geolocation.rb +16 -0
- data/lib/clickwrap/lifecycle.rb +534 -0
- data/lib/clickwrap/linter.rb +382 -0
- data/lib/clickwrap/localized_text.rb +101 -0
- data/lib/clickwrap/macros.rb +203 -0
- data/lib/clickwrap/models/application_record.rb +20 -0
- data/lib/clickwrap/models/chain_head.rb +79 -0
- data/lib/clickwrap/models/concerns/has_clickwraps.rb +55 -0
- data/lib/clickwrap/models/disposition_plan.rb +208 -0
- data/lib/clickwrap/models/document.rb +46 -0
- data/lib/clickwrap/models/document_version.rb +163 -0
- data/lib/clickwrap/models/event.rb +743 -0
- data/lib/clickwrap/models/event_document.rb +79 -0
- data/lib/clickwrap/models/event_statement.rb +92 -0
- data/lib/clickwrap/models/external_action.rb +150 -0
- data/lib/clickwrap/models/integrity_attestation.rb +90 -0
- data/lib/clickwrap/models/legal_hold.rb +81 -0
- data/lib/clickwrap/models/policy_revision.rb +115 -0
- data/lib/clickwrap/models/presentation.rb +59 -0
- data/lib/clickwrap/models/receipt_access.rb +53 -0
- data/lib/clickwrap/models/recording_sequence.rb +21 -0
- data/lib/clickwrap/models/request_evidence.rb +378 -0
- data/lib/clickwrap/models/statement_identity_lock.rb +38 -0
- data/lib/clickwrap/models/statement_state.rb +130 -0
- data/lib/clickwrap/pending_receipt.rb +177 -0
- data/lib/clickwrap/policy.rb +283 -0
- data/lib/clickwrap/presentation_manifest.rb +210 -0
- data/lib/clickwrap/presenter.rb +716 -0
- data/lib/clickwrap/privacy.rb +419 -0
- data/lib/clickwrap/protected_outcome.rb +120 -0
- data/lib/clickwrap/receipt.rb +606 -0
- data/lib/clickwrap/receipt_html.rb +235 -0
- data/lib/clickwrap/receipt_verifier.rb +978 -0
- data/lib/clickwrap/reference.rb +44 -0
- data/lib/clickwrap/registration.rb +236 -0
- data/lib/clickwrap/registry.rb +54 -0
- data/lib/clickwrap/remediation_token.rb +155 -0
- data/lib/clickwrap/request_evidence_extractor.rb +590 -0
- data/lib/clickwrap/request_evidence_policy.rb +261 -0
- data/lib/clickwrap/retention/applier.rb +231 -0
- data/lib/clickwrap/retention/disposition.rb +221 -0
- data/lib/clickwrap/retention/planner.rb +502 -0
- data/lib/clickwrap/retention_class.rb +97 -0
- data/lib/clickwrap/reviewed_text.rb +28 -0
- data/lib/clickwrap/schema_requirements.rb +196 -0
- data/lib/clickwrap/services/authorize_external_action.rb +149 -0
- data/lib/clickwrap/services/load_policies.rb +69 -0
- data/lib/clickwrap/services/publish_documents.rb +251 -0
- data/lib/clickwrap/services/validate_policy_references.rb +166 -0
- data/lib/clickwrap/statement.rb +248 -0
- data/lib/clickwrap/subject_fingerprint.rb +28 -0
- data/lib/clickwrap/submission.rb +169 -0
- data/lib/clickwrap/system_actor.rb +31 -0
- data/lib/clickwrap/test_helpers.rb +676 -0
- data/lib/clickwrap/testing.rb +211 -0
- data/lib/clickwrap/trusted_proxy_configuration.rb +92 -0
- data/lib/clickwrap/verification.rb +504 -0
- data/lib/clickwrap/version.rb +12 -1
- data/lib/clickwrap/view_helpers.rb +190 -0
- data/lib/clickwrap/vocabulary.rb +294 -0
- data/lib/clickwrap.rb +497 -7
- data/lib/generators/clickwrap/document_generator.rb +164 -0
- data/lib/generators/clickwrap/hardening_generator.rb +177 -0
- data/lib/generators/clickwrap/install_generator.rb +1287 -0
- data/lib/generators/clickwrap/link_generator.rb +56 -0
- data/lib/generators/clickwrap/policy_generator.rb +118 -0
- data/lib/generators/clickwrap/templates/clickwrap_hardening.rb.erb +256 -0
- data/lib/generators/clickwrap/templates/clickwrap_policies.rb.erb +192 -0
- data/lib/generators/clickwrap/templates/create_clickwrap_external_action_tables.rb.erb +128 -0
- data/lib/generators/clickwrap/templates/create_clickwrap_integrity_tables.rb.erb +157 -0
- data/lib/generators/clickwrap/templates/create_clickwrap_presentation_tables.rb.erb +160 -0
- data/lib/generators/clickwrap/templates/create_clickwrap_request_evidence_tables.rb.erb +180 -0
- data/lib/generators/clickwrap/templates/create_clickwrap_retention_tables.rb.erb +174 -0
- data/lib/generators/clickwrap/templates/create_clickwrap_tables.rb.erb +568 -0
- data/lib/generators/clickwrap/templates/initializer.rb.erb +439 -0
- data/lib/generators/clickwrap/templates/link_clickwrap_event_migration.rb.erb +12 -0
- data/lib/generators/clickwrap/templates/policy.rb.erb +31 -0
- data/lib/generators/clickwrap/templates/policy_test.rb.erb +56 -0
- data/lib/generators/clickwrap/templates/privacy.md.erb +58 -0
- data/lib/generators/clickwrap/templates/terms.md.erb +49 -0
- data/lib/generators/clickwrap/upgrade_generator.rb +50 -0
- data/lib/generators/clickwrap/views_generator.rb +101 -0
- data/lib/tasks/clickwrap.rake +569 -0
- metadata +311 -16
|
@@ -0,0 +1,439 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# ==============================================================================
|
|
4
|
+
# Clickwrap
|
|
5
|
+
# ==============================================================================
|
|
6
|
+
#
|
|
7
|
+
# Everything here is a decision about evidence: what gets recorded, who can read
|
|
8
|
+
# it, how long it lives, and what the receipt is allowed to claim. The comments
|
|
9
|
+
# explain the CONSEQUENCE of each setting rather than its syntax, because the
|
|
10
|
+
# person reading this file in three years will be trying to answer a question
|
|
11
|
+
# about something that happened, not looking up an argument type.
|
|
12
|
+
#
|
|
13
|
+
# ONLY DECISIONS ARE LIVE IN THIS FILE. Every setting left at the gem's default
|
|
14
|
+
# appears commented, with its default value shown — the defaults are already the
|
|
15
|
+
# safe posture (nothing personal is collected, annexes encrypted, receipt access
|
|
16
|
+
# denied until granted), and restating them as live code would dress a few dozen
|
|
17
|
+
# non-decisions up as decisions. `bin/rails clickwrap:doctor` prints the full
|
|
18
|
+
# effective posture at any time.
|
|
19
|
+
#
|
|
20
|
+
# The one deliberate exception is the request-evidence block. Every
|
|
21
|
+
# `record_*_by_default` line is live even when it says `false`, because each one
|
|
22
|
+
# is an answer to a question this installer actually asked, and "we decided not
|
|
23
|
+
# to collect this" is a decision worth reading rather than inferring from a
|
|
24
|
+
# file that does not mention it.
|
|
25
|
+
#
|
|
26
|
+
# A policy can override the request-evidence defaults below, so a high-risk
|
|
27
|
+
# authorization can collect more context without ordinary signup inheriting it.
|
|
28
|
+
#
|
|
29
|
+
# There is deliberately no `maximum_evidence`, `full_evidence`, `legal_proof`,
|
|
30
|
+
# or regulation-named mode setting. No runtime flag can make a legal
|
|
31
|
+
# determination for you, and an option that turns on a category of personal data
|
|
32
|
+
# as a side effect of something else is exactly what this gem exists not to do.
|
|
33
|
+
<%- if recipe -%>
|
|
34
|
+
#
|
|
35
|
+
# This file was scaffolded with `--request-evidence-recipe=<%= recipe %>`. The
|
|
36
|
+
# recipe expanded into the individual settings below and then stopped existing:
|
|
37
|
+
# it is a generator convenience, not a runtime mode, and nothing reads it again.
|
|
38
|
+
<%- end -%>
|
|
39
|
+
#
|
|
40
|
+
# Documents, policies, and retention classes live in config/clickwrap.rb.
|
|
41
|
+
# ==============================================================================
|
|
42
|
+
|
|
43
|
+
Clickwrap.configure do |config|
|
|
44
|
+
# --- Identity ---------------------------------------------------------------
|
|
45
|
+
#
|
|
46
|
+
# Who can act, how a controller finds them, and how they are referenced in
|
|
47
|
+
# evidence. This is the mapping every receipt depends on, so it is the one the
|
|
48
|
+
# installer refuses to guess at when it is ambiguous.
|
|
49
|
+
|
|
50
|
+
<%- if actor_class_name && !default_identity_settings? -%>
|
|
51
|
+
# DECISION — detected during install. Clickwrap checks every recorded actor
|
|
52
|
+
# against this class, so a wrong value here is refused at capture rather than
|
|
53
|
+
# attributing evidence to the wrong kind of record for years.
|
|
54
|
+
config.actor_class_name = "<%= actor_class_name %>"
|
|
55
|
+
|
|
56
|
+
# The controller method that returns the current actor. Rails 8's generated
|
|
57
|
+
# Authentication concern exposes `Current.user` rather than a `current_user`
|
|
58
|
+
# helper — if that is your stack, either define `current_user` or point this
|
|
59
|
+
# at the method you actually have.
|
|
60
|
+
config.current_actor_method_name = :<%= current_actor_method_name %>
|
|
61
|
+
<%- elsif actor_class_name -%>
|
|
62
|
+
# Detected during install, and the same as the gem's defaults — so there is
|
|
63
|
+
# nothing to override. Clickwrap checks every recorded actor against this
|
|
64
|
+
# class, so if your actor model is not <%= actor_class_name %>, set it here:
|
|
65
|
+
#
|
|
66
|
+
# config.actor_class_name = "<%= actor_class_name %>"
|
|
67
|
+
# config.current_actor_method_name = :<%= current_actor_method_name %>
|
|
68
|
+
<%- else -%>
|
|
69
|
+
# Clickwrap could not infer your actor class unambiguously, so it did not
|
|
70
|
+
# choose one for you: which record can act is a security-relevant identity
|
|
71
|
+
# mapping, and a wrong guess attributes years of evidence to the wrong kind of
|
|
72
|
+
# record. Uncomment and set both lines.
|
|
73
|
+
#
|
|
74
|
+
<%= actor_class_reason_comment %>
|
|
75
|
+
#
|
|
76
|
+
# config.actor_class_name = "User"
|
|
77
|
+
# config.current_actor_method_name = :current_user
|
|
78
|
+
<%- end -%>
|
|
79
|
+
|
|
80
|
+
# The engine's screens render inside your application: your layout, your
|
|
81
|
+
# authentication callbacks, your locale. They are only as protected as this
|
|
82
|
+
# controller is. Actor, tenant, and subject stay three separate facts and
|
|
83
|
+
# never collapse into one polymorphic id; returning nil for the tenant means
|
|
84
|
+
# "single tenant", which is correct for most applications:
|
|
85
|
+
#
|
|
86
|
+
# config.parent_controller_class_name = "ApplicationController"
|
|
87
|
+
# config.find_current_tenant_with = lambda do |controller|
|
|
88
|
+
# controller.current_organization if controller.respond_to?(:current_organization)
|
|
89
|
+
# end
|
|
90
|
+
|
|
91
|
+
# How an actor is named and snapshotted in evidence. The defaults record a
|
|
92
|
+
# stable reference that outlives row deletion — the model's own
|
|
93
|
+
# `clickwrap_actor_reference`, then a GlobalID, then a `ClassName/id` string —
|
|
94
|
+
# and snapshot NOTHING. A snapshot is permanent, so it should carry fields
|
|
95
|
+
# someone chose rather than every column that existed that day: an email
|
|
96
|
+
# copied in here is still in the receipt after the account is deleted.
|
|
97
|
+
# Override only against a reviewed identifier scheme or a reviewed field list:
|
|
98
|
+
#
|
|
99
|
+
# config.identify_actor_with = ->(actor) { "account/#{actor.public_id}" }
|
|
100
|
+
# config.snapshot_actor_with = ->(_actor) { {} }
|
|
101
|
+
|
|
102
|
+
# Authentication is recorded as context, never as an identity claim: it says
|
|
103
|
+
# which application-supplied authentication state was in force, and nothing
|
|
104
|
+
# more. The default asks the controller for the current actor and records
|
|
105
|
+
# `{ method: :authenticated_session }` only when it gets one, so a signup form
|
|
106
|
+
# — where nobody is signed in yet — describes nothing rather than claiming a
|
|
107
|
+
# session that did not exist. Override it to ADD reviewed context:
|
|
108
|
+
#
|
|
109
|
+
# config.describe_authentication_with = lambda do |_controller|
|
|
110
|
+
# session = Current.session
|
|
111
|
+
# next {} unless session
|
|
112
|
+
#
|
|
113
|
+
# { method: session.authentication_method, authenticated_at: session.created_at }
|
|
114
|
+
# end
|
|
115
|
+
|
|
116
|
+
# --- Documents and policies -------------------------------------------------
|
|
117
|
+
|
|
118
|
+
<%- if renders_documents_through_markdown_rails? -%>
|
|
119
|
+
# DECISION — your legal pages are Markdown files this application already
|
|
120
|
+
# renders through markdown-rails, so Clickwrap renders them through that same
|
|
121
|
+
# registered renderer: the snapshot people accept is byte-identical to the
|
|
122
|
+
# page they read, by construction, and sanitizing stays in the pipeline those
|
|
123
|
+
# pages already trust rather than a second pass behind the digest.
|
|
124
|
+
config.document_renderer = :markdown_rails
|
|
125
|
+
|
|
126
|
+
<%- end -%>
|
|
127
|
+
# Where published bytes live, how they become the representation people are
|
|
128
|
+
# offered, and where declarations are read from. `:database` needs nothing
|
|
129
|
+
# else to stay readable in ten years; the default renderer keeps the faithful
|
|
130
|
+
# escaped-text representation, `:markdown` renders through whichever Markdown
|
|
131
|
+
# library you already bundle, and `:markdown_rails` renders through your
|
|
132
|
+
# application's own registered renderer — which is what keeps a snapshot
|
|
133
|
+
# byte-identical to the public page of the same file. A custom renderer must
|
|
134
|
+
# return the exact bytes it offered, because Clickwrap stores their digest
|
|
135
|
+
# next to the source digest: "this Markdown file existed" and "this rendered
|
|
136
|
+
# representation was offered" stay two separate claims.
|
|
137
|
+
#
|
|
138
|
+
<%- unless renders_documents_through_markdown_rails? -%>
|
|
139
|
+
# config.document_renderer = :markdown_rails
|
|
140
|
+
<%- end -%>
|
|
141
|
+
# config.store_document_contents_in = :database
|
|
142
|
+
# config.document_resolver = nil
|
|
143
|
+
# config.policy_paths = [ "config/clickwrap.rb", "config/clickwrap/*.rb" ]
|
|
144
|
+
|
|
145
|
+
# Publishing rides `db:prepare`, so the deploy step everyone forgets stops
|
|
146
|
+
# existing: by the time the server takes traffic, every declared version has
|
|
147
|
+
# an immutable snapshot. It is idempotent, and a refusal — a reused version
|
|
148
|
+
# label over changed bytes — fails the deploy out loud instead of letting
|
|
149
|
+
# signups fail quietly some hours later. Set it to false to keep publishing a
|
|
150
|
+
# deliberate `bin/rails clickwrap:publish` step of your own. A required legal
|
|
151
|
+
# statement with no translation raises rather than render a raw I18n key
|
|
152
|
+
# beside a control someone is about to press:
|
|
153
|
+
#
|
|
154
|
+
# config.publish_documents_after_database_preparation = true
|
|
155
|
+
# config.raise_on_missing_translation = true
|
|
156
|
+
|
|
157
|
+
# How immutable document links open — never WHERE they point, which is signed
|
|
158
|
+
# into the presentation and refused if this hook tries to return `href:`. It
|
|
159
|
+
# is evaluated in the rendering view, so per-request helpers work:
|
|
160
|
+
#
|
|
161
|
+
# config.document_link_html_options_with = ->(_document) { { target: "_blank", rel: "noopener" } }
|
|
162
|
+
|
|
163
|
+
# Hotwire Native gets a declarative answer covering both halves — the signed
|
|
164
|
+
# href AND the link attributes. Inside a native authentication sheet, a
|
|
165
|
+
# same-host document link is routed by the app itself, which pops the sheet
|
|
166
|
+
# and takes the half-filled form with it; `:external_browser` absolutizes the
|
|
167
|
+
# link against your canonical host so the form is still there when the person
|
|
168
|
+
# comes back. `:same_screen` keeps ordinary same-host links for your own
|
|
169
|
+
# native path configuration to route. `open_in:` also takes a callable, for an
|
|
170
|
+
# app that needs both answers on different screens:
|
|
171
|
+
#
|
|
172
|
+
# config.hotwire_native_document_links = {
|
|
173
|
+
# open_in: :external_browser,
|
|
174
|
+
# canonical_host: "https://www.example.com"
|
|
175
|
+
# }
|
|
176
|
+
|
|
177
|
+
# --- Presentation and integrity ---------------------------------------------
|
|
178
|
+
#
|
|
179
|
+
# A signed presentation manifest binds the exact wording, versions, and call
|
|
180
|
+
# to action of the server-generated offer to the submission that followed. Its
|
|
181
|
+
# validity window is short by design — long enough for a slow reader, short
|
|
182
|
+
# enough to keep the substitution check meaningful — receipts digest with
|
|
183
|
+
# SHA-256, and the higher integrity tiers stay off until you bring an adapter.
|
|
184
|
+
# Each tier claims exactly what it supplies: the baseline detects ordinary
|
|
185
|
+
# mutation of the verified bytes, not someone who controls the database and
|
|
186
|
+
# the application.
|
|
187
|
+
#
|
|
188
|
+
# Chaining links each event to the previous one per tenant, which makes
|
|
189
|
+
# rewriting history detectable. Anchoring offers each committed chain position
|
|
190
|
+
# to an outside publication service, and a receipt upgrades only after the
|
|
191
|
+
# adapter verifies that exact publication. A timestamp adapter preserves
|
|
192
|
+
# exactly the assurance its provider supplies — Clickwrap's own
|
|
193
|
+
# `recorded_at_by_server` is named for what it is: this server's clock.
|
|
194
|
+
#
|
|
195
|
+
# The chaining and attestation tables ship with
|
|
196
|
+
# `rails generate clickwrap:install --with-integrity`.
|
|
197
|
+
#
|
|
198
|
+
# config.presentation_valid_for = 2.hours
|
|
199
|
+
# config.digest_canonical_receipts_with = :sha256
|
|
200
|
+
# config.chain_event_history_with = nil
|
|
201
|
+
# config.anchor_event_history_with = nil
|
|
202
|
+
# config.timestamp_receipts_with = nil
|
|
203
|
+
|
|
204
|
+
# Stamp every event with the code that wrote it, so a receipt can be traced to
|
|
205
|
+
# a deploy. Point these at whatever your deployment actually sets:
|
|
206
|
+
#
|
|
207
|
+
# config.application_version = -> { ENV["RELEASE_SHA"] }
|
|
208
|
+
# config.template_version = -> { nil }
|
|
209
|
+
|
|
210
|
+
# --- Authorization ----------------------------------------------------------
|
|
211
|
+
|
|
212
|
+
# DECISION — actors read their own receipts. The gem's default denies ALL
|
|
213
|
+
# receipt access until you write this check; the installer grants exactly
|
|
214
|
+
# self-service and nothing wider. A foreign id returns not found rather than
|
|
215
|
+
# leaking that it exists.
|
|
216
|
+
#
|
|
217
|
+
# The `present?` check is load-bearing: `receipt.actor` is legitimately nil
|
|
218
|
+
# for imported evidence and after an actor's account is deleted, and a
|
|
219
|
+
# signed-out viewer is nil too — a bare `==` would let nil equal nil and
|
|
220
|
+
# fail open on exactly those receipts.
|
|
221
|
+
config.authorize_receipt_access_with = lambda do |controller, receipt|
|
|
222
|
+
controller.<%= current_actor_method_name %>.present? &&
|
|
223
|
+
controller.<%= current_actor_method_name %> == receipt.actor
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
# Unredacted request evidence — the raw IP address, User-Agent, and
|
|
227
|
+
# IP-geolocation values — stays denied (the default) until you write a real
|
|
228
|
+
# check. Every granted access needs a human-readable reason and appends an
|
|
229
|
+
# access event, so "who read this and why" stays answerable. The shape a real
|
|
230
|
+
# one takes:
|
|
231
|
+
#
|
|
232
|
+
# config.authorize_unredacted_request_evidence_access_with =
|
|
233
|
+
# ->(controller, _receipt, because) { controller.current_user&.security_operator? && because.present? }
|
|
234
|
+
|
|
235
|
+
# --- Request evidence: what is recorded by default --------------------------
|
|
236
|
+
#
|
|
237
|
+
# Clickwrap always records its own event id, the server time, the capture
|
|
238
|
+
# channel, the policy and application version, the configured actor and
|
|
239
|
+
# authentication source, and the HTTP request id when one is available. None
|
|
240
|
+
# of that is personal request evidence.
|
|
241
|
+
#
|
|
242
|
+
# The settings below are, and they are the one block in this file written out
|
|
243
|
+
# in full even when every answer is "no". Each one is a separate line because
|
|
244
|
+
# each one is a separate decision about what to keep about a person, and none
|
|
245
|
+
# of them turns on as a side effect of another.
|
|
246
|
+
#
|
|
247
|
+
# IP addresses and linked online identifiers can be personal data. Keeping
|
|
248
|
+
# them on your own infrastructure does not remove purpose, transparency,
|
|
249
|
+
# minimization, security, or retention duties. Provider-estimated geolocation
|
|
250
|
+
# is network context: not identity, not GPS, not a street address, and not
|
|
251
|
+
# proof that anyone was physically anywhere.
|
|
252
|
+
<%- if records_any_request_evidence? -%>
|
|
253
|
+
#
|
|
254
|
+
# You enabled some of these during install. The generator required an explicit
|
|
255
|
+
# purpose and deletion period before writing this file; review both below. The
|
|
256
|
+
# annex table itself came with `--with-request-evidence`, which this install
|
|
257
|
+
# added for you.
|
|
258
|
+
<%- else -%>
|
|
259
|
+
#
|
|
260
|
+
# Everything here is off, which is the safe default — and the annex table was
|
|
261
|
+
# not created at all. A single policy that genuinely needs a field can still
|
|
262
|
+
# name it (add the table with
|
|
263
|
+
# `rails generate clickwrap:install --with-request-evidence`), without
|
|
264
|
+
# ordinary signup inheriting the collection.
|
|
265
|
+
<%- end -%>
|
|
266
|
+
|
|
267
|
+
config.record_ip_address_by_default = <%= record_ip_addresses? %>
|
|
268
|
+
config.record_browser_user_agent_by_default = <%= record_browser_user_agents? %>
|
|
269
|
+
<%- ip_geolocation_fields.each do |field| -%>
|
|
270
|
+
config.record_ip_geolocation_<%= field %>_by_default = <%= record_ip_geolocation_field?(field) %>
|
|
271
|
+
<%- end -%>
|
|
272
|
+
|
|
273
|
+
# --- Request evidence: why, how long, and how it is protected ---------------
|
|
274
|
+
#
|
|
275
|
+
# An enabled field with a blank purpose or no deletion rule is a configuration
|
|
276
|
+
# error at boot, not a warning. "We turned it on and never decided why" is the
|
|
277
|
+
# failure mode these settings exist to prevent. There is no keep-forever
|
|
278
|
+
# default anywhere in this gem, and deleting a value removes the encrypted
|
|
279
|
+
# annex entry and appends a disposition event — the historical agreement,
|
|
280
|
+
# declaration, or authorization stays intact and verifiable without it.
|
|
281
|
+
|
|
282
|
+
<%- if record_ip_addresses? -%>
|
|
283
|
+
config.reason_for_recording_ip_addresses_by_default =
|
|
284
|
+
<%= reason_for_recording_ip_addresses.inspect %>
|
|
285
|
+
config.delete_recorded_ip_addresses_after = <%= delete_recorded_ip_addresses_after_days %>.days
|
|
286
|
+
<%- end -%>
|
|
287
|
+
<%- if record_browser_user_agents? -%>
|
|
288
|
+
config.reason_for_recording_browser_user_agents_by_default =
|
|
289
|
+
<%= reason_for_recording_browser_user_agents.inspect %>
|
|
290
|
+
config.delete_recorded_browser_user_agents_after = <%= delete_recorded_browser_user_agents_after_days %>.days
|
|
291
|
+
<%- end -%>
|
|
292
|
+
<%- if any_ip_geolocation_field? -%>
|
|
293
|
+
config.reason_for_recording_ip_geolocation_by_default =
|
|
294
|
+
<%= reason_for_recording_ip_geolocation.inspect %>
|
|
295
|
+
config.delete_recorded_ip_geolocation_after = <%= delete_recorded_ip_geolocation_after_days %>.days
|
|
296
|
+
<%- end -%>
|
|
297
|
+
<%- if records_any_request_evidence? -%>
|
|
298
|
+
# A date to look at this configuration again, so an enabled field does not
|
|
299
|
+
# quietly outlive the reason it was turned on. `clickwrap:doctor` reports an
|
|
300
|
+
# enabled field with no review date.
|
|
301
|
+
config.review_default_request_evidence_configuration_on = <%= review_date_literal %>
|
|
302
|
+
<%- else -%>
|
|
303
|
+
# Nothing above is enabled, so there is nothing to give a purpose or a
|
|
304
|
+
# deletion period to. Enabling a field is never one line — the gem refuses an
|
|
305
|
+
# enabled field with no recorded purpose and no deletion rule — and it wants a
|
|
306
|
+
# review date so the decision gets looked at again by a person instead of
|
|
307
|
+
# outliving its reason:
|
|
308
|
+
#
|
|
309
|
+
# config.reason_for_recording_ip_addresses_by_default = "Investigate disputed submissions"
|
|
310
|
+
# config.delete_recorded_ip_addresses_after = 90.days
|
|
311
|
+
# config.review_default_request_evidence_configuration_on = Date.new(<%= Date.today.year + 1 %>, 1, 1)
|
|
312
|
+
<%- end -%>
|
|
313
|
+
|
|
314
|
+
# Your own reference to the assessment or basis you recorded elsewhere, if
|
|
315
|
+
# your jurisdiction and policy call for one. Clickwrap stores the string in
|
|
316
|
+
# the privacy inventory and the receipt; it does not choose a lawful basis,
|
|
317
|
+
# and it cannot tell you whether the one you chose is the right one:
|
|
318
|
+
#
|
|
319
|
+
# config.legal_basis_reference_for_recording_ip_addresses_by_default = nil
|
|
320
|
+
# config.legal_basis_reference_for_recording_browser_user_agents_by_default = nil
|
|
321
|
+
# config.legal_basis_reference_for_recording_ip_geolocation_by_default = nil
|
|
322
|
+
|
|
323
|
+
# Recorded values are encrypted at rest with Active Record encryption.
|
|
324
|
+
# Turning one of these off puts the raw value in plain text in your database,
|
|
325
|
+
# and therefore in every ordinary backup and database dump. It is allowed,
|
|
326
|
+
# because some applications have a reviewed reason, but it is never a quiet
|
|
327
|
+
# one-character change: Clickwrap refuses `false` until you have said why in
|
|
328
|
+
# `config.deliberately_store_request_evidence_unencrypted!(because: "…")`.
|
|
329
|
+
#
|
|
330
|
+
# config.encrypt_recorded_ip_addresses = true
|
|
331
|
+
# config.encrypt_recorded_browser_user_agents = true
|
|
332
|
+
# config.encrypt_recorded_ip_geolocation = true
|
|
333
|
+
|
|
334
|
+
# Server-observed, always. The browser cannot submit or replace these values,
|
|
335
|
+
# and a client-reported address is labeled as such rather than silently
|
|
336
|
+
# standing in for one Clickwrap saw.
|
|
337
|
+
#
|
|
338
|
+
# `request.remote_ip` is only as trustworthy as your proxy configuration. If
|
|
339
|
+
# you record IP addresses, verify and TEST `config.action_dispatch` trusted
|
|
340
|
+
# proxies behind your load balancer or CDN — otherwise you are recording a
|
|
341
|
+
# header a client controls:
|
|
342
|
+
# https://api.rubyonrails.org/classes/ActionDispatch/RemoteIp.html
|
|
343
|
+
#
|
|
344
|
+
# config.read_ip_address_from_http_request_with = ->(http_request) { http_request.remote_ip }
|
|
345
|
+
# config.read_browser_user_agent_from_http_request_with = ->(http_request) { http_request.user_agent }
|
|
346
|
+
|
|
347
|
+
<%- if trusted_proxy_configuration_digest -%>
|
|
348
|
+
# DECISION — the reviewed trusted-proxy digest you supplied to the installer.
|
|
349
|
+
# It does not make the rules or the network path correct; it records which
|
|
350
|
+
# rules were in force beside every address recorded under them.
|
|
351
|
+
config.trusted_proxy_configuration_digest =
|
|
352
|
+
<%= trusted_proxy_configuration_digest.inspect %>
|
|
353
|
+
<%- elsif records_ip_derived_request_evidence? -%>
|
|
354
|
+
# DECISION — this install records IP-derived evidence, so the digest is
|
|
355
|
+
# derived from the EFFECTIVE Rails rules (`config.action_dispatch.trusted_proxies`,
|
|
356
|
+
# or Rails' own defaults) rather than from a prose description of them. It
|
|
357
|
+
# does not make the rules correct; it records which rules were in force.
|
|
358
|
+
config.trusted_proxy_configuration_digest =
|
|
359
|
+
Clickwrap.trusted_proxy_configuration_digest_for_rails_application
|
|
360
|
+
<%- else -%>
|
|
361
|
+
# A digest of the effective trusted-proxy rules, stored beside any recorded
|
|
362
|
+
# address. Nothing here records one, so there is nothing to stamp:
|
|
363
|
+
#
|
|
364
|
+
# config.trusted_proxy_configuration_digest =
|
|
365
|
+
# Clickwrap.trusted_proxy_configuration_digest_for_rails_application
|
|
366
|
+
<%- end -%>
|
|
367
|
+
|
|
368
|
+
<%- if any_ip_geolocation_field? -%>
|
|
369
|
+
# DECISION — you enabled IP-geolocation fields and explicitly selected this
|
|
370
|
+
# resolver. Make its gem and data source available in every environment:
|
|
371
|
+
# Clickwrap refuses to boot with fields it has nothing to resolve, rather than
|
|
372
|
+
# recording blanks that later read as "no result".
|
|
373
|
+
#
|
|
374
|
+
# Only the fields authorized above are stored, never the whole result object,
|
|
375
|
+
# and every stored value keeps the provider, source, estimated state,
|
|
376
|
+
# resolution time, and any accuracy or database provenance the resolver
|
|
377
|
+
# supplies. A policy cannot keep the coordinates and drop the uncertainty
|
|
378
|
+
# needed to read them.
|
|
379
|
+
config.ip_geolocation_resolver = <%= ip_geolocation_resolver_class_name %>.new
|
|
380
|
+
<%- else -%>
|
|
381
|
+
# Required before any IP-geolocation field can be enabled, here or in a
|
|
382
|
+
# policy. The official optional resolver requires trackdown >= 0.4 so the
|
|
383
|
+
# provider that actually answered and per-request source trust are preserved:
|
|
384
|
+
#
|
|
385
|
+
# bundle add trackdown --version ">= 0.4"
|
|
386
|
+
# config.ip_geolocation_resolver = Clickwrap::IpGeolocation::TrackdownResolver.new
|
|
387
|
+
<%- end -%>
|
|
388
|
+
|
|
389
|
+
# Whether an unavailable resolver blocks capture or produces an `unavailable`
|
|
390
|
+
# state in the receipt. Blocking means a provider outage stops people from
|
|
391
|
+
# signing up; not blocking means some receipts honestly say the lookup failed.
|
|
392
|
+
# Receipts keep `not_configured`, `unavailable`, `recorded`, and `deleted`
|
|
393
|
+
# apart, so "we chose not to collect it" never blurs into "collection failed":
|
|
394
|
+
#
|
|
395
|
+
# config.fail_capture_when_ip_geolocation_is_unavailable = false
|
|
396
|
+
|
|
397
|
+
# --- Hooks ------------------------------------------------------------------
|
|
398
|
+
#
|
|
399
|
+
# These run only after the required evidence and the domain state have
|
|
400
|
+
# committed. A failure here is reported and can never undo the committed
|
|
401
|
+
# action — and a hook can never stand in for one either.
|
|
402
|
+
#
|
|
403
|
+
# config.after_event_is_committed = lambda do |event|
|
|
404
|
+
# Marketing::StopProcessingJob.perform_later(event.actor_id) if event.consent_was_withdrawn?
|
|
405
|
+
# end
|
|
406
|
+
|
|
407
|
+
# DECISION — after-commit hook failures land in your error reporter instead of
|
|
408
|
+
# vanishing. The gem's default reports nothing, because it has no reporter to
|
|
409
|
+
# assume.
|
|
410
|
+
config.report_after_commit_failure_with = ->(error, _event) { Rails.error.report(error) }
|
|
411
|
+
|
|
412
|
+
# --- Development aids -------------------------------------------------------
|
|
413
|
+
#
|
|
414
|
+
# In development and test, every render is scanned for the mistakes a form can
|
|
415
|
+
# make silently: a preselected consent control, a consent sentence carrying
|
|
416
|
+
# two purposes, a document link below the submit button, a missing
|
|
417
|
+
# presentation token. Findings go to the log and never raise — a lint finding
|
|
418
|
+
# is a thing to look at, not a reason to stop a page from rendering — and it
|
|
419
|
+
# is off in production, because a production request has no business scanning
|
|
420
|
+
# its own HTML on the way out. `nil` means "decide from the environment":
|
|
421
|
+
#
|
|
422
|
+
# config.lint_presentations = nil
|
|
423
|
+
|
|
424
|
+
# --- Retention calculations -------------------------------------------------
|
|
425
|
+
#
|
|
426
|
+
# A duration alone cannot express every schedule, so a retention class can
|
|
427
|
+
# name a host event instead ("the later of five years after capture and three
|
|
428
|
+
# years after the subject was closed") and register the calculation here.
|
|
429
|
+
# Returning nil is a legitimate answer: it means the triggering event has not
|
|
430
|
+
# happened yet, so the record is reported as unresolved rather than given an
|
|
431
|
+
# invented date.
|
|
432
|
+
#
|
|
433
|
+
# config.calculate_retention_time_for :regulated_evidence_retention_ends do |event|
|
|
434
|
+
# [
|
|
435
|
+
# event.recorded_at_by_server + 5.years,
|
|
436
|
+
# event.subject_liquidated_at&.+(3.years)
|
|
437
|
+
# ].compact.max
|
|
438
|
+
# end
|
|
439
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
class <%= migration_class_name %> < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>]
|
|
2
|
+
# Which exact clickwrap capture authorized each row of <%= table_name %>:
|
|
3
|
+
# one column, pointing at a verifiable receipt. Nullable because rows that
|
|
4
|
+
# predate the gem have no event; ULID string to match clickwrap_events'
|
|
5
|
+
# primary key; no foreign key on purpose — evidence and domain rows keep
|
|
6
|
+
# independent retention schedules, and neither table may block the other's
|
|
7
|
+
# disposition.
|
|
8
|
+
def change
|
|
9
|
+
add_column :<%= table_name %>, :clickwrap_event_id, :string, limit: 26
|
|
10
|
+
add_index :<%= table_name %>, :clickwrap_event_id
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# ==============================================================================
|
|
4
|
+
# The :<%= policy_key %> policy
|
|
5
|
+
# ==============================================================================
|
|
6
|
+
#
|
|
7
|
+
# A policy is a server-owned offer: exactly what the server will generate, and
|
|
8
|
+
# exactly what it will accept back. It compiles at boot, so a mistake here is a
|
|
9
|
+
# startup failure with an explanation rather than a surprise during a signup.
|
|
10
|
+
#
|
|
11
|
+
# Render it with:
|
|
12
|
+
#
|
|
13
|
+
# <%%= form.clickwrap :<%= policy_key %>, submit: "Your call to action" %>
|
|
14
|
+
#
|
|
15
|
+
# Capture it with `Clickwrap.capture!`, or with `Clickwrap.capture_and!` when a
|
|
16
|
+
# domain action must commit in the same transaction as its evidence.
|
|
17
|
+
# ==============================================================================
|
|
18
|
+
|
|
19
|
+
Clickwrap.policy :<%= policy_key %> do
|
|
20
|
+
# The verb controls the lifecycle, so the generator required you to choose it
|
|
21
|
+
# explicitly. The statement is the exact first-person sentence stored in the
|
|
22
|
+
# receipt; review both as product/legal copy, not as incidental labels.
|
|
23
|
+
<%= policy_verb %> :<%= statement_key %>,
|
|
24
|
+
<% statement_options.each_with_index do |option, index| -%>
|
|
25
|
+
<%= option %><%= "," unless index == statement_options.length - 1 %>
|
|
26
|
+
<% end -%>
|
|
27
|
+
|
|
28
|
+
# This class must already exist in a Clickwrap.retention declaration. The
|
|
29
|
+
# generator names it; Clickwrap never invents or silently defaults a period.
|
|
30
|
+
retain_with :<%= retention_class_key %>
|
|
31
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "test_helper"
|
|
4
|
+
|
|
5
|
+
# A policy is executable meaning, so it is worth testing like one. These are the
|
|
6
|
+
# four questions worth asking about any policy. The first is complete because
|
|
7
|
+
# the generator knows the exact inputs you gave it; the remaining application
|
|
8
|
+
# behavior stays skipped until you connect a real actor and flow.
|
|
9
|
+
#
|
|
10
|
+
# Add `include Clickwrap::TestHelpers` to your ActiveSupport::TestCase (or keep
|
|
11
|
+
# it here) to get `capture_clickwrap` and the assertions below. They create
|
|
12
|
+
# real, internally consistent evidence without your tests knowing table names.
|
|
13
|
+
class <%= test_class_name %> < ActiveSupport::TestCase
|
|
14
|
+
include Clickwrap::TestHelpers
|
|
15
|
+
|
|
16
|
+
# 1. It compiles, and it says what you think it says.
|
|
17
|
+
#
|
|
18
|
+
# This is the cheap test that catches a renamed document, a deleted statement,
|
|
19
|
+
# or a verb someone changed without meaning to.
|
|
20
|
+
test "the policy is defined with the statements it is supposed to have" do
|
|
21
|
+
policy = Clickwrap.policy!(:<%= policy_key %>)
|
|
22
|
+
|
|
23
|
+
assert_equal ["<%= statement_key %>"], policy.statements.map(&:key)
|
|
24
|
+
assert_equal ["<%= evidence_kind %>"], policy.statements.map(&:kind)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# 2. A complete submission produces the evidence you expect.
|
|
28
|
+
test "capturing it records the acts and verifies" do
|
|
29
|
+
skip "connect this test to your actor fixture or factory and submit the real answer"
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# 3. An incomplete submission records NOTHING.
|
|
33
|
+
#
|
|
34
|
+
# The interesting half of any evidence policy is what it refuses. A missing
|
|
35
|
+
# required answer must not produce a partial event that later reads as assent.
|
|
36
|
+
test "it refuses an incomplete submission" do
|
|
37
|
+
skip "connect this test to your actor fixture or factory"
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# 4. The evidence and the protected action commit together, or not at all.
|
|
41
|
+
#
|
|
42
|
+
# Only relevant if this policy guards a domain action through `capture_and!`.
|
|
43
|
+
# Fault injection is the point: if the event write fails, the action must roll
|
|
44
|
+
# back, and a failed evidence write must never leave a usable domain record
|
|
45
|
+
# behind.
|
|
46
|
+
test "a failed evidence write rolls the protected action back" do
|
|
47
|
+
# Clickwrap::Testing.fail_next_event_write do
|
|
48
|
+
# assert_raises(Clickwrap::EventWriteFailed) do
|
|
49
|
+
# # ...the call that captures evidence and performs the action...
|
|
50
|
+
# end
|
|
51
|
+
# end
|
|
52
|
+
#
|
|
53
|
+
# assert_no_clickwrap_event :<%= policy_key %>
|
|
54
|
+
skip "fill this in if this policy protects a domain action"
|
|
55
|
+
end
|
|
56
|
+
end
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Privacy Notice
|
|
3
|
+
last_updated: <%= Date.today.iso8601 %>
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# PLACEHOLDER — this is not your Privacy Notice
|
|
7
|
+
|
|
8
|
+
**Replace this entire file before you present it to anyone.**
|
|
9
|
+
|
|
10
|
+
The `clickwrap` gem generated this placeholder so that `config/clickwrap.rb` has
|
|
11
|
+
a file to point at and `bin/rails clickwrap:publish` has bytes to digest. It is
|
|
12
|
+
not a privacy notice, and it describes no actual processing.
|
|
13
|
+
|
|
14
|
+
Clickwrap deliberately never writes legal text. Only you know what your
|
|
15
|
+
application collects, why, on what basis, who it goes to, and for how long — and
|
|
16
|
+
a notice the gem invented would describe an application that does not exist.
|
|
17
|
+
|
|
18
|
+
Your notice has to describe what your application actually does, which now
|
|
19
|
+
includes whatever `clickwrap` records on your behalf. By default that is the
|
|
20
|
+
evidence event itself: an identifier, your server's time, the capture channel,
|
|
21
|
+
the policy and application version, the configured actor reference and
|
|
22
|
+
authentication context, and the HTTP request id when one is available.
|
|
23
|
+
|
|
24
|
+
If you enabled anything in the request-evidence section of
|
|
25
|
+
`config/initializers/clickwrap.rb` — the IP address, the browser User-Agent, or
|
|
26
|
+
any provider-estimated IP-geolocation field — those are personal data too, and
|
|
27
|
+
your notice is where you tell people about them. `bin/rails
|
|
28
|
+
clickwrap:privacy:inventory` lists exactly what your configuration records,
|
|
29
|
+
for which stated purpose, under which retention rule. It reports your
|
|
30
|
+
configuration; it does not judge it.
|
|
31
|
+
|
|
32
|
+
## What to do now
|
|
33
|
+
|
|
34
|
+
1. Replace this file with your own reviewed Privacy Notice, keeping the front
|
|
35
|
+
matter at the top of it.
|
|
36
|
+
2. Keep `last_updated:` accurate. That line is the version label: the
|
|
37
|
+
declaration in `config/clickwrap.rb` carries no `version:` and reads the
|
|
38
|
+
label from here, so this file names its own version and there is no second
|
|
39
|
+
copy of the label to drift. Use `clickwrap_version:` — it wins over
|
|
40
|
+
`last_updated:` — when a same-day correction changes the bytes and the date
|
|
41
|
+
readers see has to stay put.
|
|
42
|
+
3. Run `bin/rails clickwrap:publish` to freeze an immutable snapshot. Deploys do
|
|
43
|
+
this for you: publishing rides `db:prepare`.
|
|
44
|
+
4. Bump the label and publish again whenever the text changes, so a receipt
|
|
45
|
+
keeps binding the notice version in the accepted server offer.
|
|
46
|
+
|
|
47
|
+
A Markdown `config.document_renderer` strips the front-matter block from the
|
|
48
|
+
representation people are shown. The source digest covers this file's exact
|
|
49
|
+
bytes either way, front matter included.
|
|
50
|
+
|
|
51
|
+
Note how this document is used in `config/clickwrap.rb`: it is `acknowledge`,
|
|
52
|
+
not `agree_to` and not `consent_to`. A notice is information people are entitled
|
|
53
|
+
to receive, not a contract term they assent to or permission they grant.
|
|
54
|
+
Anything you genuinely need consent for gets its own separate, unselected,
|
|
55
|
+
withdrawable consent statement.
|
|
56
|
+
|
|
57
|
+
If this text ever reaches a real person, something went wrong: check that this
|
|
58
|
+
file was replaced, and that `clickwrap:publish` ran against the replacement.
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Terms of Service
|
|
3
|
+
last_updated: <%= Date.today.iso8601 %>
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# PLACEHOLDER — this is not your Terms of Service
|
|
7
|
+
|
|
8
|
+
**Replace this entire file before you present it to anyone.**
|
|
9
|
+
|
|
10
|
+
The `clickwrap` gem generated this placeholder so that `config/clickwrap.rb` has
|
|
11
|
+
a file to point at and `bin/rails clickwrap:publish` has bytes to digest. It is
|
|
12
|
+
not terms. It is not a draft of terms. It is not a starting point for terms.
|
|
13
|
+
|
|
14
|
+
Clickwrap deliberately never writes legal text. It has no idea who you are, what
|
|
15
|
+
you sell, where you operate, who your users are, or which rules apply to any of
|
|
16
|
+
that — and text that merely *looked* plausible would be worse than this file,
|
|
17
|
+
because someone would ship it.
|
|
18
|
+
|
|
19
|
+
What the gem does own is the mechanics around whatever you put here: the exact
|
|
20
|
+
bytes and their digest, the version and locale, the wording and call to action
|
|
21
|
+
in the server-generated offer, the submitted answer, the time your server
|
|
22
|
+
recorded it, and a receipt that can be reproduced and verified years later.
|
|
23
|
+
What the words say, whether they are fair, whether they are complete, and
|
|
24
|
+
whether they do what you need them to do is yours and your counsel's.
|
|
25
|
+
|
|
26
|
+
## What to do now
|
|
27
|
+
|
|
28
|
+
1. Replace this file with your own reviewed Terms of Service, keeping the front
|
|
29
|
+
matter at the top of it.
|
|
30
|
+
2. Keep `last_updated:` accurate. That line is the version label: the
|
|
31
|
+
declaration in `config/clickwrap.rb` carries no `version:` and reads the
|
|
32
|
+
label from here, so the file that holds the words is also the file that names
|
|
33
|
+
its version and there is no second copy of the label to drift. A label is
|
|
34
|
+
yours to choose; reusing one for different bytes is refused rather than
|
|
35
|
+
accepted. When a same-day correction has to change the bytes without moving
|
|
36
|
+
the date readers see, add `clickwrap_version:` — it wins over
|
|
37
|
+
`last_updated:` — and leave the date alone.
|
|
38
|
+
3. Run `bin/rails clickwrap:publish` to freeze an immutable snapshot. Deploys do
|
|
39
|
+
this for you: publishing rides `db:prepare`.
|
|
40
|
+
4. Bump the label and publish again whenever the text changes. Published bytes
|
|
41
|
+
are never edited in place — that is what lets a receipt from three years ago
|
|
42
|
+
reproduce the exact document version its accepted server offer bound.
|
|
43
|
+
|
|
44
|
+
A Markdown `config.document_renderer` strips the front-matter block from the
|
|
45
|
+
representation people are shown. The source digest covers this file's exact
|
|
46
|
+
bytes either way, front matter included.
|
|
47
|
+
|
|
48
|
+
If this text ever reaches a real person, something went wrong: check that this
|
|
49
|
+
file was replaced, and that `clickwrap:publish` ran against the replacement.
|